9.4 KiB
Semantic Catalog Reconciliation
Document status: Canonical reconciliation ledger (TASK-17025 gate + TASK-17039 collapse)
Date: 2026-08-11
Source of truth: src/catalog/canonical-operations.ts (CAPABILITY_CANONICAL_OPERATIONS) — the single hand-authored 41-operation list. Both catalog modules derive their operation set, placement, required claims, and task-mode policy from it.
Drift authority: src/catalog/reconciliation.ts (machine-readable ledger), drift-checked by src/catalog/reconciliation.test.ts and src/catalog/catalog-docs.test.ts.
Parent plan: Runner foundation review plan (TASK-17016), deliverable D and execution step 1.
Purpose
The runner carried two independently hand-maintained semantic catalogs. TASK-17025
established one machine-readable authority over both and recorded every
divergence. TASK-17039 performed the physical collapse: the authoritative
operation list, placement, required claims, and task-mode policy now live once in
src/catalog/canonical-operations.ts, and both catalog modules derive from it —
neither declares an independent operation list or an independent claim/mode
policy any longer. Each catalog supplies only its per-surface presentation
(title, description, input/output schema, and the scenario-only mock mapping and
redaction rules), and a bidirectional parity check fails the build if either
catalog's presentation set drifts from the canonical surface.
The two catalogs
| Catalog | Module | Count | Descriptor | Consumers |
|---|---|---|---|---|
| Scenario / eval | src/tools/ |
37 (14 always + 23 optional) | Rich: placement (disposition), sideEffectClass, idempotency, redaction, mockCommandMapping |
src/scenarios/*, src/conformance/*; package-root default export |
| Live runtime | src/semantic-tools/ |
28 (13 always + 15 optional) | Live: exposure, allowedModes, disabledByDefault, provider input/output schemas; runtime dispatcher + policy + redaction |
src/live/* (live Codex), src/issue-thread/*, generated semantic-tool-contracts.json; package-root acceptedCapabilitySemanticTools |
Reconciled op-set relationship (pinned by the drift test):
- 24 shared operation ids.
- 13 scenario-only:
administer_company,export_company,inspect_operation_result,list_cases,list_company_skills,list_goals,list_projects,list_routines,list_secret_metadata,manage_routine,read_secret_value,sync_company_skills,upsert_case. - 4 live-only:
get_agent,get_approval,get_approval_context,schedule_wake. - Union = 41 operations.
Canonical authority decision
- Single source of truth:
src/catalog/canonical-operations.tshand-authors the 41-op union exactly once (CAPABILITY_CANONICAL_OPERATIONS), each entry naming surfaces, placement, optional group, claims, task modes, roles, side-effect class, idempotency, disabled-by-default, real-binding status, PRP evidence, and legacy aliases. Both catalog modules import it and project it:src/tools/renders the rich scenario descriptor andsrc/semantic-tools/renders the live provider descriptor. - Drift ledger:
src/catalog/reconciliation.tsenriches the canonical list with the scenario mock mapping and redaction flag (CAPABILITY_CANONICAL_CATALOG), andcapabilityCatalogReconciliation()recomputes the op-set summary and any remaining metadata divergence between the two catalogs' actual descriptors. - Neither
src/tools/norsrc/semantic-tools/is canonical; both are projections ofcanonical-operations.ts.
Real-binding status (before real Paperclip service binding)
No operation is bound to a real Paperclip service yet; the deterministic mock is the only backend (real-service binding is deliverable G). Each operation's current executability is classified as:
live_codex(27): executed by the live dispatcher against the mock and exposed to live Codex — the 24 shared ops (minus the test-only escape hatch) plus the 4 live-only ops.scenario_mock(13): defined only in the scenario/eval catalog and driven through the scenario runtime / mock extensions; no live dispatcher binding.test_only(1):generic_api_request— the escape hatch, which by rule cannot count as product capability coverage.
Divergence ledger (after the TASK-17039 collapse)
requiredClaims and task modes are single-sourced from
canonical-operations.ts, so the two catalogs cannot diverge on them; the drift
test asserts both divergence sets are now empty. The reconciliation ledger
recomputes divergences from the two catalogs' actual descriptors:
- Claims (0): unified.
generic_api_requestcarries a single claim,test:generic_api_request, on both surfaces. - Task modes (0): unified to one canonical policy — the live runtime
exposure policy unioned with
skill_test. Reads stay open in every mode; work-writes (report_progress,write_document,request_human_input,register_deliverable) allowstandard/planning/skill_test; lifecycle and state writes (finish_task,block_task,request_review,create_task,set_dependencies, request/decide/comment approvals,control_workspace_service) allowstandard/skill_test. Theskill_testmode makes every operation the eval harness must drive drivable, while the live runtime keeps its intended per-mode exposure. - Input-schema shape (reviewed intentional projection): one input contract is
projected two ways by design — the live provider descriptor threads
idempotencyKeyin-band with provider length/pattern bounds for the model, while the observable scenario runtime threads idempotency out-of-band. This is a single reviewed projection recorded in the ledger, not two independently authored schemas.
Effect on live provider schemas (QA gate)
- The generated live contract golden
generated/capability/semantic-tool-contracts.jsonis byte-identical after the collapse: it serializes name, description, input/output schema, exposure, and required claims — none of which changed on the live surface (allowedModesis not serialized). - The live runtime
allowedModesgainedskill_testfor the nine previouslystandard-only operations (finish_task,block_task,request_review,create_task,set_dependencies,request_approval,decide_approval,control_workspace_service,schedule_wake). This is a behavioral change to eval-harness exposure only; real live tasks never run inskill_testmode.
Legacy MCP aliases
Legacy MCP aliases are indexed in spec/capability/mcp-tool-map.yaml
(inventoryRole: legacy_alias_index, generated from packages/mcp-server). Each
alias row carries a foldedInto disposition linking it to the native eval/
operation that now covers its behavior; the reconciliation drift test asserts
every row has one. The authority additionally records the direct native
operation for the well-known aliases (for example paperclipListAgents →
list_agents, paperclipGetAgent → get_agent, paperclipListIssues →
search_tasks, paperclipCreateIssue → create_task).
Collapse decisions executed by TASK-17039
- One source of truth.
src/tools/andsrc/semantic-tools/no longer declare independent operation lists; both projectCAPABILITY_CANONICAL_OPERATIONS. Placement, optional group, required claims, task modes, allowed roles, side-effect class, idempotency, and disabled-by-default are single-sourced. Bidirectional parity checks fail the build on any drift between a catalog's presentation and the canonical surface. generic_api_requestclaim unified totest:generic_api_request(scenario callers/tests updated). It staystest_onlyand is excluded from product capability coverage.- One canonical task-mode policy (live runtime policy ∪
skill_test); the ledger's 15 task-mode divergences are resolved to zero. - Input schema unified as one contract, projected two ways — idempotency in-band with provider bounds for the model, out-of-band for the observable scenario runtime — recorded as the single reviewed intentional projection.
- Placement of scenario-only (13) and live-only (4) operations retained. The
13 scenario-only ops stay
scenario_mock(eval-only via mock extensions); the 4 live-only reads/continuation ops (get_agent,get_approval,get_approval_context,schedule_wake) staylive_codexwith no scenario projection. No operation crossed surfaces this increment; a future real-service binding (deliverable G) is where scenario-only ops would gain a live binding.
Consumed by the real-surface-ledger (deliverable B) and eval (deliverable F)
tasks: the live contract golden is unchanged; the only behavioral delta is the
allowedModes skill_test addition described above.
Verification
src/catalog/reconciliation.test.ts— pins the op-set relationship, asserts both catalogs derive their operation set from the canonical source, asserts claims/modes/placement are single-sourced (no divergence), and requires a reviewed disposition for the surviving input-schema projection.src/catalog/catalog-docs.test.ts— recomputes the catalog counts and membership indocs/capability-semantic-tools.mdfrom the catalog so a stale hand count fails.- Live golden byte-identity checked by
check:semantic-contracts; the scenario and live runtime behavior is covered by thetest:scenariossuites.