Skip to content

petrova.cupel.trace_status — read-only tool scope

Date: 2026-09-10 Status: open Supersedes: none Superseded-by: none — current

stratt-hq/stratt-run PR #334 (docs/decisions/2026-09-10-cupel-petrova-integration-points.md, that repo) scopes CUPEL↔PETROVA to three additive extension points, all petrova-codes-side, none implemented. Item 1 (mr_grounding.kind += cupel_ticket/cupel_trace) shipped here as #419. This doc scopes item 3: a new read-only verb/MCP tool letting a petrova caller query traces/TR-*.yaml and handoffs/HO-*.yaml state for a target repo — no existing petrova.* tool touches these paths.

Read from the stratt-run source directly this session: scripts/check-cupel-trace-lint.ts (TR-.yaml shape, T1-T6/T8-T10), scripts/check-cupel-handoff.ts (HO-.yaml shape, state_assertions re-derivation), scripts/check-cupel-trailer.ts (Cupel-Red/Cupel-Green/Cupel-Criterion commit trailer format). Neither traces/ nor handoffs/ exist yet in that repo (one handoff, HO-smo1-census-1.yaml, landed same PR) — this tool has no live fixture beyond that one file today.

Add petrova.cupel.trace_status (read-only, mutating: false) to host/src/registry.ts, backed by a new host/src/sources/cupel.ts source, following the existing remote-repo-read pattern used by acts.ts/audit.ts (makeOctokitForOwner(owner) + octo.repos.getContent({owner, repo, path, ref}) resolved from registry.yaml by slug) rather than local filesystem (eva.ts’s pattern only works for repos checked out as siblings, which stratt-hq is but the tool must not assume that for every fleet member).

Schema (schemas: { type: "object", required: ["slug"], properties: { slug: {type:"string"}, criterion: {type:"string"}, ticket: {type:"string"} } }):

  • slug (required) — registry slug, resolved to owner/repo the same way acts.get does.
  • criterion (optional) — filter traces to one criterion.id (e.g. C1).
  • ticket (optional) — filter handoffs to one ticket.

Behavior:

  1. List traces/*.yaml and handoffs/*.yaml via getContent on the directory path (404 on either → treat as empty list, not an error — both check scripts already model “no dir yet” as a pass-0 state, this tool should match that posture).
  2. Parse each YAML (js-yaml, already a host dependency via eva.ts).
  3. For each trace record, surface: criterion.id, invariant, whether red/green blocks are both present, red.at < green.at (T3), whether merge.commit is set and its cites includes both run-ids (T6) — i.e. report the same violations check-cupel-trace-lint.ts computes, but as read-only status fields, not a pass/fail gate. Do not reimplement T1-T10 exhaustively; surface the subset a caller needs to know “is this criterion green and merged” (T3, T6, presence of red/green) and flag lint_clean: null with a note that full T1-T10 requires running the actual script, rather than silently claiming a clean bill this tool didn’t fully check.
  4. For each handoff record, surface: handoff_id, ticket, phase, count of state_assertions, and reproduced: null (this tool must NOT re-run derive commands against a remote repo it doesn’t have checked out — re-derivation requires local execution, which is check-cupel-handoff.ts’s job, not a read-only MCP tool’s). Report structure only, never claim reproduction.
  5. Degrade-never-throw contract, matching ralph.run/ralph.runs: auth or rate-limit failure returns { ok:false, degraded:true, detail }, not an exception.

Explicitly out of scope for this tool (mirrors the parent decision’s altitude split):

  • No derive re-execution (that requires a local checkout + shell exec against a repo this MCP server doesn’t own).
  • No admissibility/criteria-count judgment (CUPEL’s own scripts own that).
  • No write path — this is query-only, symmetric with petrova.ralph.run and petrova.decisions.get.
  • Reimplement full T1-T10 trace-lint inside the tool — rejected: duplicates check-cupel-trace-lint.ts logic in a second language-adjacent surface that will drift; the parent decision doc already rejected merging CUPEL discipline into petrova at a finer grain than citation/status.
  • Local filesystem read via EvaSource-style root path — rejected: only works for repos already checked out as workspace siblings; the tool must work for any registered fleet slug, same constraint acts.ts/audit.ts solve via makeOctokitForOwner.
  • Fold into petrova.acts.recent/diagnose — rejected per the parent decision’s “diagnose is not being merged with census” ruling; this is a distinct namespace (petrova.cupel.*) for a distinct, optional-adoption substrate.

For code:

  • New file host/src/sources/cupel.ts.
  • New registry entry in host/src/registry.ts (read-only surface — no spec/verbs/*.schema.json file needed; existing read-only tools like petrova.decisions.get don’t have one either, only the 18 mutating/CLI verbs under spec/verbs/ do).
  • No schema changes beyond #419 (already shipped).

For docs:

  • This doc. No CLAUDE.md changes — tool count in the top-of-file “43 tools” line will need bumping to 44 once implemented (currently unimplemented).

For in-flight phases: none.

For invariants: none changed.

  • stratt-hq/stratt-run PR #334, docs/decisions/2026-09-10-cupel-petrova-integration-points.md
  • stratt-hq/stratt-run scripts/check-cupel-trace-lint.ts, check-cupel-handoff.ts, check-cupel-trailer.ts
  • host/src/sources/acts.ts, host/src/sources/audit.ts (remote-repo-read pattern to follow)
  • host/src/registry.ts (registration point)
  • petrova-codes #419 (feat(spec): add cupel_ticket/cupel_trace grounding kinds)
  • Subagent: agent (Claude Sonnet 5), composed 2026-09-10
  • Human countersign: alex@devarno.com — scope agreed, implementation cleared to start

Countersigned by human:alex@devarno.com on 2026-09-10 — commit 909ecf52 (this file’s creation commit) is authored by the human directly, per docs/decisions/2026-09-05-proxy-countersign-retired.md’s post-retirement definition. Line added retroactively by the agent as scribe.