Skip to content

tracker operations

The exact fields each tracker.* operation takes and returns.

Every op is POST /v1/<op> over HTTP and the MCP tool loom_<op>. The identity fields (org, namespace, agent, user_id, session_id) are filled from the token and the MCP session when a transport can; for an HTTP call with no token on a single-user server, the install's org on every op but code.* and tracker.* (which keep their plane's), and on some ops its namespace and agent; see identities and namespaces. The operations index explains role, kind and the MCP all listing. Unknown input fields are served and named in ignored_args.

tracker.answer

Answer a tracker question in prose — or from an artefact's anchors — by running the catalogue's templates: escalation / incident context, a customer across channels, incidents / escalations…

POST /v1/tracker.answer · MCP loom_tracker_answer · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str '' The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
question str '' The question in prose; may be empty when anchors are given.
anchors dict[str, Any] (computed) Structured anchors a client already holds: a PR's components and paths, an issue number and its frames, a customer, a version, a window, intent words — {numbers, number_kind, versions, components, paths, handles, customers, tests, terms…
limit int 10 Rows per section, at most; the server caps it at 50.
explain_only bool False Return the plan (sections marked planned) without reading the tracker.
Output field Type Meaning
intent str What the planner read the question as (e.g. release_readiness); "" when unclear.
anchors dict[str, Any] The anchors the plan used: the prose's merged with the ones passed in.
sections list[dict[str, Any]] [{template, title, access, params, why, status served|empty|restricted_required| tracker_unavailable|planned, cites, rows}]
searched list[str] one string per section: template(params) — what was searched
lines list[str] Prose lines of the served sections with inline citations; empty with explain_only.
restricted bool True when the answering tracker namespace is restricted.
verdict str planned | answered | empty | unknown (no anchor to plan on)

tracker.binding

What this namespace may ask the tracker plane: a QA namespace answers itself; a code namespace answers the tracker namespace bound to it (LOOM_TRACKER_CODE_NAMESPACE_BINDINGS_JSON), passed through…

POST /v1/tracker.binding · MCP loom_tracker_binding · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str '' The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
Output field Type Meaning
tracker_namespace str the namespace QA questions on the addressed namespace are answered by; "" when there is none the caller may read
tracker_namespaces list[str] Every tracker linked to the addressed code namespace the caller may read, primary (tracker_namespace) first; the searches read them all, a lookup by number reads the primary — or address another by name
restricted bool True when the answering tracker namespace is restricted.
kind str qa: the addressed namespace is a tracker namespace | code: a code namespace bound to one the caller may read | none: nothing to ask (unbound, refused, or neither — the three are one answer, so a refusal names nothing)
authorized bool True when the caller may ask a tracker here; False with kind = none.
templates list[str] catalogue template names the caller may run: public, plus the restricted class when restricted

tracker.component

One component's risk snapshot and its open issues, most severe first.

POST /v1/tracker.component · MCP loom_tracker_component · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str '' The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
component str required Component name, matched exactly as the tracker spells it.
limit int 10 At most this many open issues; the server caps it at 50.
Output field Type Meaning
component str Echo of the requested component.
risk dict[str, Any] The component's latest risk snapshot row (columns as the source holds them); {} when none.
open_issues list[dict[str, Any]] Open issues in the component, most severe first: number, title, severity, labels, versions, bisect outcome, flaky/crash flags, url.
known bool True when the tracker has a risk row or an open issue for the component.
tracker_unavailable bool True when the source could not be read; the fields above are then empty, not a claim.

tracker.contract

The record contract as the register page shows it before anyone connects: every table loom reads, required or optional, each column's family, the type a view writes, whether a default is safe, what…

POST /v1/tracker.contract · MCP loom_tracker_contract · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The org.
database str '<database>' The database the templates name (default a placeholder).
Output field Type Meaning
database str The database name the templates use (the placeholder <database> when none valid was given).
tables list[dict[str, Any]] [{table, required, note, columns: [{name, family, type, must_map, note}]}]
templates dict[str, str] Table name -> a CREATE OR REPLACE VIEW template with every column at its typed default.

tracker.duplicate_check

Does this draft already exist?

POST /v1/tracker.duplicate_check · MCP loom_tracker_duplicate_check · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str '' The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
title str '' The draft's title, as a reporter would file it.
body str '' The draft's body text.
frames list[str] (computed) qualified frames or raw trace lines, each at most 4,000 characters; normalised the way code.trace parses
component str '' The draft's component; boosts candidates with the same component.
version str '' The version the draft is about (X.Y, a longer one is cut to its minor); boosts candidates affecting it.
mentions list[int] (computed) issue / PR numbers the draft refers to, beside the ones its text names
paths list[str] (computed) the finding's code-path evidence: repository paths a fix PR of a candidate may have touched (fix_path), each at most 4,000 characters
symbols list[str] (computed) Code symbols from the finding, each at most 4,000 characters, matched like stack frames.
issue_number int 0 the issue already filed for this draft (its own text is the query when title and body are empty; it is excluded; its recorded relations are read)
top_k int 10 At most this many items; the server caps it at 50.
as_of str '' ISO 8601: only items created before it are candidates (replays); '' = now
namespaces list[str] (computed) The tracker namespaces to read (each a tracker namespace or a code namespace bound to one), the addressed one first; empty = every QA record the caller may read when addressed to a code namespace or the bound one, a single read when a…
related_prs bool True Also search the PRs that may already address the draft (on none / related). loom_search's tracker leg turns it off: its 3 s budget has no room for the extra search
rerank bool True Bisect's own check runs the legs alone — the rerank's LLM call does not fit the bisect's budget
Output field Type Meaning
verdict str duplicate_of | related | none — none carries no items (file it)
items list[dict[str, Any]] [{kind, number, repo, url, title, state, state_reason, component, created_at, score, confidence (null when uncalibrated), relation: duplicate | related, relation_recorded: duplicate_of | related_to | fixed_by | none (the QA graph's edge to…
undecided bool the top candidate sits in [related, duplicate) AND shares the component: route the list to a human
epistemic str calibrated (a stored fit scored the items) | uncalibrated (no fit: related at most, confidence null) | degraded (a leg, or one of several trackers, did not answer: a none is not clearance) | unavailable (nobody read the record)
thresholds dict[str, float] {duplicate, related} — the served thresholds on the probability scale
judge dict[str, Any] The judge stage — ran, model, tokens, labels per candidate number, the numbers it promoted; absent when the stage is off
fit_at str When the stored calibration fit was computed; "" when no fit is stored.
fit dict[str, Any] {status served | no_fit, fit_at, queries {n, positives, negatives}, auc, holdout_auc, tracker_namespace}
query dict[str, Any] what was searched: terms, frames and their source, mentions, component, versions, error_code, the wall, the legs' timings
legs dict[str, str] per leg: served | empty | skipped | not_computed | tracker_unavailable
leg_status list[TrackerLegStatus] Per leg of every namespace read: status ok | degraded | skipped, reason and message, retried, ms.
tracker_unavailable bool every leg failed to read: the verdict above is not a claim
namespaces list[str] The tracker namespaces this answer read (bound one first) and each one's own verdict / item count / epistemic / legs; every item in items names its namespace.
by_namespace dict[str, dict[str, Any]] Per namespace read: verdict, item count, epistemic, undecided, legs and tracker_unavailable.
closest list[dict[str, Any]] On none / related, up to three rows the fit scored under the related line whose title is near-identical to the draft's — [{namespace, kind, number, title, state, url, confidence, title_similarity, evidence}]; the verdict above stays…
needs list[dict[str, str]] the draft inputs the stored fit rewards and the draft left empty: [{input: body | paths | frames | version | component, what, feature}]
closest_note str one sentence: the same-titled record, why the fit did not call it, what to pass
nearest list[dict[str, Any]] On none / related, the fit's best scored-out issues that share a distinctive term with the draft, up to three rows shown in all (served items count) — [{namespace, number, title, state, url, confidence, why}].
rerank dict[str, Any] The rerank stage — ran, model, prompt, pool size, picks [{number, label: same|related, confidence}], tokens, ms, and the HyDE scan's own block; a pick leads nearest and never changes the verdict
answer str One sentence — already reported? fixed by which PR, in which release? or a PR that may already address it.
related_prs list[dict[str, Any]] On none / related, PRs that may already address the draft (code.pr_check) — [{number, title, state, relation, score, merged_at}]

tracker.flaky

Is this test a known flaky?

POST /v1/tracker.flaky · MCP loom_tracker_flaky · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str '' The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
test_name str required The test to look up: flaky-test issues whose title holds every token of it match.
limit int 10 At most this many issues; the server caps it at 50.
include_signal bool False Also run tracker.test_signal on the test and return it in signal.
window_days int 0 History window for signal, in days; 0 = the deployment default.
Output field Type Meaning
test_name str The test name asked about, trimmed.
known_flaky bool True when at least one flaky-test issue names the test.
open_issues int How many of issues are still open.
issues list[dict[str, Any]] Matching flaky-test issues, newest first: number, title, state, labels, dates, url.
verdict str flaky_by_issue | no_flaky_signal | tracker_unavailable — the second is silence, not "not flaky"; the third is "nobody looked"
tracker_unavailable bool True when the source could not be read; verdict is then tracker_unavailable.
signal dict[str, Any] \| None The tracker.test_signal answer, only when asked for

tracker.item

The tracker record of up to ten issue / PR numbers: component, severity, bisect verdict and branches still affected, test presence, CI result, reviewers, backport lineage, and the fixes / backports…

POST /v1/tracker.item · MCP loom_tracker_item · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str '' The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
numbers list[int] required Issue or PR numbers to read (1–10); duplicates fold and numbers <= 0 are dropped.
Output field Type Meaning
items list[dict[str, Any]] One entry per known number: kind, state, title, author, dates, url, the tracker record (tracker), a one-line summary (line) and the files each fixed_by PR touched.
unknown list[int] numbers the tracker source has no row for — unknown, not absent (the upstream sync lags minutes)
tracker_unavailable bool the source could not be read (breaker open, budget exceeded, credential unset, kill switch): nothing above is a claim

Which PR probably fixed this issue — or which issue this PR probably fixed — when nobody wrote the link down.

POST /v1/tracker.links · MCP loom_tracker_links · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str '' The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
issue_number int 0 The issue whose probable fixing PRs to list; wins over pr_number. 0 = none.
pr_number int 0 The PR whose probably-fixed issues to list, when issue_number is 0.
limit int 10 At most this many candidates; the server caps it at 50.
include_record bool True Also read the tracker graph's fixes edges live and mark candidates it confirms.
Output field Type Meaning
subject dict[str, Any] {kind: issue | pr, number}
verdict str candidates | none | not_computed — none: a list was computed and holds no row above the floor (never "no fix exists"); not_computed: the pass has not covered this number (outside its window, or never ran)
items list[dict[str, Any]] [{issue_number, pr_number, rank, score, why: [closes|mentions, lexical, frames, component], features {bm25_title, bm25_body, bm25_norm, window_hours, component_match, frames_symbols, frames_files, frames_total, closes_mention}, terms…
epistemic str Always candidate: these are computed guesses, not recorded links.
record dict[str, Any] the tracker graph's fixes edges for the subject:
computed_at str the table's last write for this namespace (what not_computed is relative to)
tracker_unavailable bool the record leg could not be read; the candidates above still stand

tracker.release

Release readiness for one version (26.3): the blocker rows with their status and the latest health snapshot.

POST /v1/tracker.release · MCP loom_tracker_release · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str '' The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
version str required The release to check, as the tracker names it (e.g. 26.3).
Output field Type Meaning
version str Echo of the requested version.
blockers list[dict[str, Any]] The release's rows: ref, status, issue_state, fix_version and active (an open Blockers row); active ones first.
active_blockers int How many of blockers are active (status Blockers and the issue still open).
health dict[str, Any] Latest health snapshot: open_blockers, critical_untested, total_items, snapshot_date; {} when none.
tracker_unavailable bool True when the source could not be read; the lists above are then empty, not a claim.

tracker.similar

The reports that already say it: duplicate and related issues / PRs for a pasted report (text), an existing item (issue_number — its own title, body, component, versions and error code become the…

POST /v1/tracker.similar · MCP loom_tracker_similar · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str '' The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
text str '' A pasted report to search for; may be empty when issue_number or frames is given.
issue_number int 0 An existing item whose own text is the query; only older items are candidates. 0 = none.
frames list[str] (computed) qualified frames or raw trace lines, each at most 4,000 characters; normalised the way code.trace parses
kinds list[str] (computed) issue | pr | incident (incidents only through a restricted tracker namespace)
top_k int 10 At most this many items; the server caps it at 50.
legs list[str] (computed) the legs to run — lexical, fields, frames, mentions, vector; the default is all (the bench arms one at a time; fields are the equality boosts on the lexical leg and run only with it; mentions are the items the text names by number)
Output field Type Meaning
items list[dict[str, Any]] [{kind, number, title, state, state_reason, score, why: [lexical|frames| mentions|vector, component, version, error_code, duplicate_of| related_to|fixed_by], relation: duplicate_of|related_to|fixed_by|none (what the tracker graph records…
query dict[str, Any] what was searched: the query item (number, kind, known), the selected terms, the frames and where they came from (caller | body), the wall; with the vector leg: fresh_items (items changed upstream since the pass's cursor, embedded on…
legs dict[str, str] per leg: served | empty | skipped | restricted_required | zero_embedding | not_computed | tracker_unavailable — a leg that did not run is named, never silent (not_computed: the vector table holds no row for the namespace — the…
epistemic str Always lower_bound: a miss does not prove no similar report exists.
tracker_unavailable bool every leg failed to read: nothing above is a claim
namespaces list[str] The trackers searched, primary first, when the addressed repository links more than one (each item then names its namespace)

tracker.source.add

Register a tracker source: a ClickHouse endpoint and database holding the record contract (docs/features/tracker/record-contract.md), a read-only credential, and the tracker namespaces that read it.

POST /v1/tracker.source.add · MCP loom_tracker_source_add · role viewer · write · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The org registering the source.
name str required Source name: 2–32 lower-case letters, digits, - or _.
host str required ClickHouse host (a URL is accepted; only the host is kept).
port int 8443 HTTPS port (8443 on ClickHouse Cloud).
secure bool True TLS.
database str required The database holding the contract views.
user str required A READ-ONLY user (SELECT on the views and system.columns).
password str required Stored encrypted at rest; never returned.
namespaces list[TrackerSourceNamespaceSpec] required The tracker namespaces reading this source.
mapping dict[str, Any] \| None None Per logical table (github_issues, github_prs, edges, …): the team's table (source) and, per logical column, an expression over its columns — a column name, {lit}, {concat}, {coalesce}, {eq}, {in}, {has}, {cast}, {lower}.
dry_run bool False Run every check and write nothing; a schema that fails the contract is returned with view_templates instead of refused.
Output field Type Meaning
name str The registered source's name, normalised.
host str The stored host (only the host part of a URL).
database str The database the source reads.
namespaces list[dict[str, Any]] [{namespace, repos, classification, join_policy}]
contract dict[str, Any] The record-contract report (ok, missing tables and columns, type mismatches, …).
roles list[str] Roles the credential holds; the read-only check reads their grants too.
my_role str admin when the caller becomes the namespaces' first admin; "" with auth off.
dry_run bool A dry run says what it would write and writes nothing; view_templates = table → a CREATE OR REPLACE VIEW draft for each required table the schema fails on ({} when it meets the contract)
written bool True when the source and its namespaces were stored; False on a dry run.
view_templates dict[str, str] On a dry run: table -> a view draft for each required table the schema fails; {} when it meets the contract or a mapping was given.

tracker.source.check

Run the record-contract check on a configured tracker source now: the same read the boot gate runs, its verdict cached.

POST /v1/tracker.source.check · MCP loom_tracker_source_check · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The org.
name str required The source name.
Output field Type Meaning
name str The source checked, normalised.
database str The database the source reads.
reachable bool False when the contract read failed or took over 8 s; contract then says why.
contract dict[str, Any] The record-contract report; {ok: None, summary} when unreachable.
view_templates dict[str, str] The view drafts for each required table the schema fails on

tracker.source.inspect

Connect with a read-only credential and say what the database holds — every table the user can read, its columns and types — plus a first mapping of it onto loom's logical model.

POST /v1/tracker.source.inspect · MCP loom_tracker_source_inspect · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The org.
name str '' A registered tracker to inspect with its own credential.
host str '' ClickHouse host (a URL is accepted).
port int 8443 HTTPS port of the ClickHouse endpoint (8443 on ClickHouse Cloud); ignored with name.
secure bool True Connect over TLS; ignored with name.
database str '' The database to inspect.
user str '' A read-only user.
password str '' Never stored by inspect.
repos list[str] (computed) The repositories the tracker holds (Org/repo); one lets a table without a repository column still build 'Org/repo#number' references.
Output field Type Meaning
host str The host inspected (only the host part of a URL).
database str The database inspected.
tables list[dict[str, Any]] [{table, engine, rows, columns: [{name, type}]}]
roles list[str] Roles the credential holds; the read-only check reads their grants too.
proposal dict[str, Any] {mode: views|mapping, roles: {logical: table}, fields: {logical: {column: {from, confidence}}}, mapping: mapping.SourceMapping JSON | None}

tracker.source.list

The tracker sources of an org: every configured source with its contract verdict, breaker and freshness, and the tracker namespaces reading it — each with the directory row's effective join rule and…

POST /v1/tracker.source.list · MCP loom_tracker_source_list · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The org whose tracker sources to list.
fresh bool False Re-read the registry now (after your own add).
Output field Type Meaning
sources list[dict[str, Any]] [{name, origin: config|registry, host, port, database, configured, contract: {ok: bool|None, summary, checked_at, optional_present[], features{}}, breaker: {state, consecutive_failures}, freshness: {issues, prs, last_refresh_at} (a…
self_service bool Whether tracker.source.add/rotate/remove answer on this deployment (LOOM_TRACKER_SELF_SERVICE_ENABLED) — the page offers the form only when they do

tracker.source.remove

Retire a registered tracker source and its namespaces.

POST /v1/tracker.source.remove · MCP loom_tracker_source_remove · role viewer · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The org.
name str required The source name.
confirm str required Type the source name to confirm.
Output field Type Meaning
name str The source retired.
deactivated bool True when the source's registry row was marked inactive; False when unknown.
namespaces_retired list[str] The source's tracker namespaces that were deleted; one that failed is left out.

tracker.source.rotate

Replace a registered source's credential.

POST /v1/tracker.source.rotate · MCP loom_tracker_source_rotate · role viewer · write · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The org.
name str required The source name.
user str required The new read-only user.
password str required Stored encrypted at rest; never returned.
Output field Type Meaning
name str The source whose credential was replaced.
contract dict[str, Any] The record-contract report read with the new credential.
rotated_at str When the credential was replaced, ISO UTC.

tracker.source.set_mapping

Change a registered tracker's mapping, with the same checks as add; mapping: null returns it to the views case.

POST /v1/tracker.source.set_mapping · MCP loom_tracker_source_set_mapping · role viewer · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The org.
name str required The tracker's name.
mapping dict[str, Any] \| None None As tracker.source.add's mapping.
dry_run bool False Check and write nothing.
Output field Type Meaning
name str The tracker source changed.
contract dict[str, Any] The record-contract report for the new mapping (ok, missing tables and columns, …).
dry_run bool True when the call only checked.
written bool True when the new mapping was stored; False on a dry run.

tracker.test_signal

Flaky, regression or infrastructure?

POST /v1/tracker.test_signal · MCP loom_tracker_test_signal · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str '' The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
test_name str required The test to read, as CI names it; an unknown name answers unknown_test with the tests it matches.
window_days int 0 History window in days; 0 = the deployment default.
code_namespace str '' Code namespace whose PR ledger churn and smells are read from; "" = the deployment's; the caller needs read access to it.
include_churn bool True Read the churn of the test's home file(s) and mapped sources; False skips it.
pr_number int 0 The PR head whose previous-outcome streak is read (0 = none)
include_smells bool True The static smell flags from the test's own source chunks
Output field Type Meaning
test_name str The test the answer is for, trimmed (the matched test when resolved_from is set).
verdict str flaky | regression_candidate | infra_candidate | no_signal | unknown_test | tracker_unavailable — no_signal is silence, not health; unknown_test is a name no test carries, with the tests it matches
why list[str] Plain sentences giving the reasons for verdict.
lower_bound float Wilson 95 % lower bound of the failure rate over the window's runs: the least the test can plausibly fail; small n → small bound
window_days int The window actually read, in days (the default when 0 was asked).
since str Start of the window, ISO UTC (hour-truncated).
features dict[str, Any] runs, fails, fail_rate, flips, steps, flip_rate, entropy, rerun_flips, prs, failing_prs, branch_fails, last_true_fail(+_head), last_fail_at, first_run_at, fail_hours
infra dict[str, Any] {k, min_prs, storm_hours, hours, share, available}
churn dict[str, Any] {available, days, home_paths, home_prs, home_recent, source_paths, source_prs, source_recent, code_namespace | reason}
thresholds dict[str, Any] The deployment thresholds the verdict used (min runs, flip rate, co-failure knobs).
source str where the history came from: the checks source clause without its credentials
timings_ms dict[str, int] Milliseconds spent per leg (history, co_failures, churn, smells, …).
tracker_unavailable bool True when the history could not be read, or an unread leg decides the verdict; verdict is then tracker_unavailable.
rollup dict[str, Any] What the daily roll-up covered of the window
streak dict[str, Any] The previous-outcome streak of (pr_number, test) —
smells dict[str, Any] Flaky-test smell tokens counted over the test's source
prior dict[str, Any] {next_run_on_pr, flake_indicators, flake_prior}
unavailable_reason str Why the source could not be read; "" when it was.
candidates list[dict[str, Any]] On unknown_test, the rolled tests whose names start with (else contain) the asked one, most runs first — [{test_name, runs, fails}] — and how many match in all; never summed into one test's features
candidate_matches int On unknown_test: how many rolled tests match the name in all.
resolved_from str The asked name when the answer is for the only test it matched