mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-26 10:26:26 +01:00
docs(runner): record semantic-catalog reconciliation ledger and alias mapping
Add spec/capability/catalog-reconciliation.md: the canonical authority decision, the 37/28/24/13/4/41 inventory, real-binding-status classification, the divergence ledger with dispositions, legacy MCP alias handling, and the deliberate decisions the schema-migration follow-on and the surface-ledger/eval tasks must execute. Also fix a fabricated legacy-alias id (paperclipAddComment) and drift-check that every alias named by the authority is a real inventory row. Refs PAP-17025. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
3d6a45e4d7
commit
4d8297128d
3 files changed
+133
-1
No files matched your search
@@ -0,0 +1,124 @@
|
||||
# Semantic Catalog Reconciliation
|
||||
|
||||
**Document status:** Canonical reconciliation ledger (PAP-17025)<br>
|
||||
**Date:** 2026-08-11<br>
|
||||
**Authority:** `src/catalog/reconciliation.ts` (machine-readable), drift-checked by `src/catalog/reconciliation.test.ts` and `src/catalog/catalog-docs.test.ts`.<br>
|
||||
**Parent plan:** Runner foundation review plan (PAP-17016), deliverable D and execution step 1.
|
||||
|
||||
## Purpose
|
||||
|
||||
The runner carried two independently hand-maintained semantic catalogs. This is
|
||||
the first reconciliation gate of the foundation plan: establish one
|
||||
machine-readable authority over both, name every operation's full metadata, and
|
||||
record every divergence with an explicit disposition so no specification calls
|
||||
either catalog canonical and no stale count or unreviewed divergence can land.
|
||||
|
||||
## 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
|
||||
|
||||
- **Descriptor schema:** the rich `src/tools/` descriptor is the canonical shape.
|
||||
It is the only one that already names placement, side-effect class,
|
||||
idempotency, redaction, and mock mapping — the per-operation facts the plan
|
||||
requires. The canonical authority extends it with **real-binding status** and
|
||||
**PRP-evidence shape**.
|
||||
- **Machine-readable authority:** `src/catalog/reconciliation.ts` enumerates the
|
||||
41-op union exactly once (`CAPABILITY_CANONICAL_CATALOG`), each entry naming
|
||||
placement, claims, task modes, roles, side-effect class, idempotency,
|
||||
redaction, mock mapping, real-binding status, PRP evidence, and legacy
|
||||
aliases. `capabilityCatalogReconciliation()` computes the op-set summary and
|
||||
the divergence ledger.
|
||||
- No spec should call either `src/tools/` or `src/semantic-tools/` canonical.
|
||||
Both remain in place this increment; the follow-on collapses them into
|
||||
derivations of the authority.
|
||||
|
||||
## 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
|
||||
|
||||
For every shared operation the authority compares `requiredClaims`, task modes,
|
||||
and input-schema shape, and records a disposition. The drift test fails if a new
|
||||
divergence appears without one.
|
||||
|
||||
- **Claims (1):** `generic_api_request` requires `test:generic_api` (scenario)
|
||||
vs `test:generic_api_request` (live). Same reviewed test-only grant; unify in
|
||||
the follow-on.
|
||||
- **Task modes (15):** `block_task`, `comment_on_approval`,
|
||||
`control_workspace_service`, `create_task`, `decide_approval`, `finish_task`,
|
||||
`get_workspace_runtime`, `list_agents`, `list_approvals`,
|
||||
`register_deliverable`, `report_progress`, `request_approval`,
|
||||
`request_review`, `search_tasks`, `set_dependencies`. The scenario catalog
|
||||
scopes modes to the coverage the suite drives (adds `skill_test`, restricts
|
||||
optional reads to `standard`); the live catalog encodes the runtime exposure
|
||||
policy (opens optional reads to `ask`/`planning`, keeps terminal writes
|
||||
`standard`-only). Reconcile to one canonical policy in the follow-on.
|
||||
- **Input-schema shape (shared mutations and some reads):** the scenario
|
||||
descriptor keeps idempotency out-of-band and omits provider bounds; the live
|
||||
provider descriptor threads `idempotencyKey` in-band and adds length/pattern
|
||||
bounds for the model. Unify the schema in the follow-on.
|
||||
|
||||
## 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`).
|
||||
|
||||
## Deliberate decisions recorded for the surface-ledger and eval tasks
|
||||
|
||||
This increment makes **no runtime behavior change** — both catalogs keep their
|
||||
exact current surfaces, so the live golden and scenario suites are unchanged.
|
||||
The reconciliation records the following decisions to be executed by the
|
||||
schema-migration follow-on and consumed by the real-surface-ledger (deliverable
|
||||
B) and eval (deliverable F) tasks:
|
||||
|
||||
1. Collapse `src/tools/` and `src/semantic-tools/` catalog lists into
|
||||
derivations of `src/catalog/reconciliation.ts`, so there is one source of
|
||||
truth and the second module is compatibility-only.
|
||||
2. Unify the `generic_api_request` claim to a single test-only grant.
|
||||
3. Choose one canonical task-mode policy per operation (the ledger's 15 task-mode
|
||||
divergences).
|
||||
4. Unify the input-schema shape (idempotency handling and provider bounds) —
|
||||
this changes the live provider schemas the model sees and the generated
|
||||
`semantic-tool-contracts.json` golden, and therefore requires live/browser
|
||||
re-QA.
|
||||
5. Decide the placement of the 13 scenario-only and 4 live-only operations on the
|
||||
unified surface (which become always/optional, which stay scenario/eval-only,
|
||||
which gain a live binding).
|
||||
|
||||
## Verification
|
||||
|
||||
- `src/catalog/reconciliation.test.ts` — pins the op-set relationship, requires a
|
||||
reconciliation entry and full metadata for every union operation, classifies
|
||||
real-binding status, and requires a disposition for every divergence.
|
||||
- `src/catalog/catalog-docs.test.ts` — recomputes the catalog counts and
|
||||
membership in `docs/capability-semantic-tools.md` from the catalog so a stale
|
||||
hand count fails.
|
||||
@@ -135,10 +135,18 @@ describe("canonical semantic-catalog reconciliation authority", () => {
|
||||
const inventory = JSON.parse(raw.replace(/^#.*$/m, ""));
|
||||
expect(inventory.inventoryRole).toBe("legacy_alias_index");
|
||||
expect(inventory.rows.length).toBeGreaterThan(0);
|
||||
const aliasIds = new Set<string>(inventory.rows.map((row: { id: string }) => row.id));
|
||||
for (const row of inventory.rows) {
|
||||
// Every alias is either folded into a native eval/operation or explicitly
|
||||
// dispositioned; a bare alias without a disposition fails the gate.
|
||||
expect(typeof row.foldedInto === "string" && row.foldedInto.length > 0).toBe(true);
|
||||
}
|
||||
// Every legacy alias named by the canonical authority must be a real
|
||||
// inventory row, so a native-mapping note cannot reference a phantom alias.
|
||||
for (const operation of CAPABILITY_CANONICAL_CATALOG) {
|
||||
for (const alias of operation.legacyAliases) {
|
||||
expect(aliasIds.has(alias)).toBe(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -110,7 +110,7 @@ const RECONCILIATION: Readonly<Record<string, ReconciliationRow>> = {
|
||||
list_documents: { realBindingStatus: "live_codex" },
|
||||
read_document: { realBindingStatus: "live_codex" },
|
||||
list_document_revisions: { realBindingStatus: "live_codex" },
|
||||
report_progress: { realBindingStatus: "live_codex", legacyAliases: ["mcp:paperclipCommentOnIssue"] },
|
||||
report_progress: { realBindingStatus: "live_codex", legacyAliases: ["mcp:paperclipAddComment"] },
|
||||
answer_status_question: { realBindingStatus: "live_codex" },
|
||||
write_document: { realBindingStatus: "live_codex" },
|
||||
request_human_input: { realBindingStatus: "live_codex" },
|
||||
|
||||
Reference in new issue
Block a user