@journeykit/canon 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -78,6 +78,11 @@ function resolveTargetPackageName() {
78
78
  return "core-linux-x64";
79
79
  return null;
80
80
  }
81
+ if (process.platform === "win32") {
82
+ if (arch === "x64")
83
+ return "core-win32-x64";
84
+ return null;
85
+ }
81
86
  return null;
82
87
  }
83
88
  function resolveRustTargetTriple() {
@@ -150,5 +155,10 @@ if (!binary) {
150
155
  console.error(" build from source: cargo build --release -p canon-cli");
151
156
  process.exit(1);
152
157
  }
153
- var result = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
154
- process.exit(result.status ?? 1);
158
+ var bundledSkills = join(cliDir, "dist", "skills");
159
+ var checkoutSkills = join(workspaceRoot, "canon", "skills");
160
+ var skillsSource = existsSync(join(bundledSkills, "SKILL.src.md")) ? bundledSkills : checkoutSkills;
161
+ var result = spawnSync(binary, process.argv.slice(2), {
162
+ stdio: "inherit",
163
+ env: { ...process.env, CANON_SKILLS_SOURCE: skillsSource }
164
+ });
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: canon
3
+ description: The provider-neutral Canon companion skill for agents using the canon CLI. Routes setup, authoring, validation, storage, ingestion, learning, retrieval, reporting, and provider-specific lazy references.
4
+ ---
5
+
6
+ # Canon
7
+
8
+ Use this single skill when working with Canon in a consumer repository. Canon's CLI remains explicitly modular; choose the command that matches the operation rather than asking an implicit router to guess.
9
+
10
+ ## Setup and provider projection
11
+
12
+ ```bash
13
+ canon init
14
+ canon skills install # detects .claude/.codex/.omp/.pi; if none exists, installs Claude+Codex
15
+ canon skills install --providers=claude,codex,omp,pi
16
+ canon skills check # read-only drift check
17
+ canon skills doctor # diagnostics; never deletes user files
18
+ ```
19
+
20
+ The canonical bundle is read-only. Claude receives
21
+ `.claude/skills/canon/SKILL.md`; Codex receives the flattened
22
+ `.codex/skills/canon.md`; OMP receives `.omp/skills/canon/SKILL.md`; and Pi
23
+ receives `.pi/skills/canon/SKILL.md`. Every selected provider receives matching
24
+ `reference/**` and `scripts/**` sidecars under its `canon` bundle. OMP and Pi
25
+ projections are project-local passive skill bundles: the
26
+ `scripts/canon-retrieve-pre-dispatch.sh` file is available as a sidecar, not
27
+ installed as a native hook. Load only the reference needed for the current
28
+ task. `canon/skills-dev` is a separate developer-only legacy source and is
29
+ installed explicitly with `--source canon/skills-dev`.
30
+
31
+ ## Route by task
32
+
33
+ - **Start product work / pin a durable unit:** read `reference/canon-subject.md`, then run `canon context` and `canon subject`.
34
+ - **Write or reorganize `.feature` specs:** read `reference/canon-authoring.md` before editing; use the scaffold.
35
+ - **Understand available fields/enums/policy bindings:** read `reference/canon-context.md` and `reference/canon-vocab.md`.
36
+ - **Capture replayable run inputs / version prompts:** read `reference/canon-context.md`, then use `canon context-pack create|show|verify` and `canon prompt register|show` with explicit repository-relative JSON manifests.
37
+ - **Validate or index specs:** read `reference/canon-fmt.md` and `reference/canon-inventory.md`.
38
+ - **Run evidence gates, flip tasks, install hooks:** read `reference/canon-gate.md`.
39
+ - **Ingest artifacts or sessions:** read `reference/canon-artifact-ingest.md`, `reference/canon-session-ingest.md`, or `reference/canon-plan-import.md`.
40
+ - **Configure storage, policy, plugins, or strategy learning:** read `reference/canon-storage.md`, `reference/canon-policy.md`, `reference/canon-plugins.md`, or `reference/canon-learn.md`.
41
+ - **Retrieve dispatch guidance:** read `reference/canon-retrieve.md` and load `scripts/canon-retrieve-pre-dispatch.sh` when wiring hooks.
42
+ - **Read generated reports and dashboards:** read `reference/canon-report-dashboard.md`.
43
+
44
+ ## Safety contract
45
+
46
+ `canon format --check`, `canon skills check`, and `canon gate check` are read-only. Do not hand-edit generated reports, provenance lines, locks, or task evidence. Inspect `canon context` before authoring typed artifacts, and treat every failure class as actionable evidence rather than bypassing the gate.
@@ -0,0 +1,72 @@
1
+ # canon-artifact-ingest
2
+
3
+ `canon ingest artifacts` turns a repo's canon-native ARTIFACTS — the
4
+ outputs of review/divergence/task/handoff work — into the regime-keyed
5
+ verdict trajectories that strategy memory and the reward gate consume
6
+ (see `canon-learn`). One pass runs the four adapters, normalizes, derives
7
+ verdicts, and writes idempotently.
8
+
9
+ ## `canon ingest artifacts [--repo <dir>] [--watch]`
10
+
11
+ ```bash
12
+ canon ingest artifacts # one pass over the repo's artifacts
13
+ canon ingest artifacts --repo ../svc # a specific repo
14
+ canon ingest artifacts --watch # poll loop
15
+ ```
16
+
17
+ Source roots resolve from `canon.yaml`'s `artifacts.sources` config
18
+ (`openspec_root`, ledger/divergence/handoff roots) — generic, never a
19
+ hardcoded path; ordinarily the consumer repo's own roots. Unconfigured =
20
+ no scan for that family.
21
+
22
+ ## The four adapters
23
+
24
+ Each reads one artifact family and emits a normalized event keyed by that
25
+ family's join key:
26
+
27
+ - **`divergence`** — parity/divergence reports → verdict keyed by the
28
+ lane derived from the report's on-disk path.
29
+ - **`ledger`** — review ledger records → verdict; the record's
30
+ trust-level tag (`@reviewed`/`@ratified`) rides through as a
31
+ passthrough `trust_level` field, not collapsed into one "success"
32
+ bucket. (The other three adapters carry no trust-level concept.)
33
+ - **`openspec_task`** — `tasks.md` checkbox rows: a `- [ ]` → `- [x] … —
34
+ ✅ <evidence>` flip, or a `**DEFERRED**`/`**DROPPED**` rewrite, keyed by
35
+ `task_id` (`<change_id>#<n>`). The evidence string is parsed for a
36
+ mergeable PR / CI reference where present (CI-first).
37
+ - **`handoff`** — handoff transitions, keyed by `handoff_id`.
38
+
39
+ ## Verdict derivation
40
+
41
+ Verdict derivation is a pure function from a normalized event to an
42
+ optional verdict `{role, polarity, becomes}`:
43
+
44
+ - A matched mapping row → a verdict for that role.
45
+ - No mapped row → NO verdict. A prose-only task flip, a deferred/dropped
46
+ task, or a handoff transition alone produces none — deliberate, not a
47
+ gap.
48
+
49
+ ## Idempotence — a re-ingest is a no-op
50
+
51
+ Write-identity is content-digest based: the digest derives both the
52
+ `regime_key` `<hash>` segment and the trajectory write-identity. Before
53
+ writing, the driver does a regime-key existence check — a second ingest
54
+ over an unchanged corpus re-derives the identical id and persists zero
55
+ new trajectories (reported as `trajectories_skipped_duplicate`).
56
+
57
+ ## Reading a role's own verdict stream
58
+
59
+ Verdicts persist regime-keyed (`<role>/<repo>/<area>/<hash>`), so a
60
+ role-scoped agent reads only its own lane — the same regime namespacing
61
+ `canon retrieve --role <r> --regime <k>` and `canon learn` query. This
62
+ verdict stream is the evidence the reward gate scores when deciding
63
+ promotion/demotion.
64
+
65
+ ## What this skill does NOT cover
66
+
67
+ - Session/cost ingestion (`canon ingest sessions`) — a different
68
+ pipeline; see the `canon-session-ingest` skill.
69
+ - Strategy promotion/demotion mechanics — see the `canon-learn` skill;
70
+ this skill only produces the verdict evidence promotion reads.
71
+ - The `regime_key` grammar itself — see `canon-retrieve`, which derives
72
+ the same key via the `canon regime-key` serializer.
@@ -0,0 +1,155 @@
1
+ # canon-authoring
2
+
3
+ A `.feature` corpus is the join spine everything else in canon hangs
4
+ off: `canon inventory sync` indexes it, `canon review`/`canon evidence`
5
+ attest against it, `canon gate check` measures coverage over it. A
6
+ corpus that is organized badly does not fail `canon format` — it
7
+ produces a green gate that measures the wrong thing. This skill is the
8
+ organizing rule.
9
+
10
+ ## Vocabulary
11
+
12
+ | Term | Is | Is NOT |
13
+ |---|---|---|
14
+ | **area** | One bounded context of the product, a kebab noun (`checkout`, `world-map`, `dm`). The directory `area=<area>/`. | A sprint, a team, a theme ("experience", "quality"). |
15
+ | **surface** | One screen, flow, or API a user or caller actually meets (`cart`, `place-lock`, `session-create`). Exactly ONE `.feature` file per surface. | A grab-bag ("misc", "experience", "review") — see anti-patterns. |
16
+ | **scenario** | One observable behavior of that surface: a refusal, a transition, a boundary, a derived value, an idempotence property. Readable by someone who has never seen the code. | A function name, an implementation step, a rule about a document or a report. |
17
+ | **`<area>.<surface>.<nn>`** | The scenario's durable id. Assigned once, never renumbered; gaps are fine. | A sort key you tidy up. |
18
+
19
+ A `Feature:` title names the surface. If you cannot name the surface,
20
+ you do not have one yet.
21
+
22
+ ## Lanes: what kind of requirement is this?
23
+
24
+ Every scenario is one of a small set of lanes, tagged `@lane:<value>`:
25
+
26
+ | Lane | The scenario is about | Attested by |
27
+ |---|---|---|
28
+ | `behavior` | What the product does — state, input, data, navigation, errors. The default. | A test or an observed run. |
29
+ | `design` | How it looks and moves — tokens, spacing, motion, easing. | A design review. |
30
+ | `architecture` | How the code is shaped — layering, ownership, error surfacing. | An architecture review. |
31
+ | `process` | How the TEAM works — what a review report may claim, how acceptance is run, what a build must check. | Nothing in the product. |
32
+
33
+ `process` rules are real, but they have no code surface and can never
34
+ be `faithful` from a test. Keep them out of the coverage number:
35
+
36
+ ```yaml
37
+ # .canon/policy.yaml
38
+ spec_coverage:
39
+ require_evidence: true
40
+ scope: [building, verifying]
41
+ exclude_lanes: [process]
42
+ ```
43
+
44
+ The base set comes from `canon.core`; a repo declares its own `lane`
45
+ enum in `.canon/vocab/<id>/enums.yaml` to replace it (see
46
+ `canon-vocab`). `canon context` shows the set in force. A value outside
47
+ a declared set is refused at the scaffold and dropped, with a counted
48
+ diagnostic, at `canon inventory sync`.
49
+
50
+ **The test for a `behavior` scenario:** name the test or run that would
51
+ attest it. If you can't, it is a `process` rule, or it is a gap — record
52
+ the gap, do not write the scenario.
53
+
54
+ ## Subject pinning
55
+
56
+ Every scenario belongs to a Subject (the durable product unit, see
57
+ `canon-subject`). Write the Subject first, then pin with `--subject`;
58
+ `canon inventory sync` carries the tag onto the index record and
59
+ `spec_coverage.scope` filters on it. An unpinned scenario is invisible to
60
+ a scoped gate.
61
+
62
+ ## The commands — and the one thing you never type
63
+
64
+ ```bash
65
+ canon feature new checkout.cart --title "Cart" # once per surface
66
+ canon scenario new checkout.cart.01 --title "Adding an item raises the count" \
67
+ --subject checkout-core --lane behavior # once per behavior
68
+ canon format specs # clean?
69
+ canon inventory sync # index it
70
+ ```
71
+
72
+ - `--actor <id>` (or `CANON_ACTOR` in the environment) puts YOUR id on the
73
+ provenance line. Default is `canon-scaffold`.
74
+ - **Never hand-type, move, or edit a `# canon:` line.** The scaffold
75
+ writes it directly above the scenario's tags:
76
+
77
+ ```gherkin
78
+ # canon: {"schema":1,"at":"2026-09-16T10:02:11Z","actor":{"agent_id":"Main"}}
79
+ @subject:checkout-core
80
+ @lane:behavior
81
+ @checkout.cart.01
82
+ Scenario: Adding an item raises the count
83
+ Given an empty cart
84
+ When one item is added
85
+ Then the count reads 1
86
+ ```
87
+
88
+ A line directly UNDER the header (older corpora) is also accepted.
89
+ Anywhere else — separated by a blank line, above `Feature:` with
90
+ scenario tags in between — is `missing-provenance`, and the whole
91
+ root's sync aborts.
92
+ - Steps are yours to write; the scaffold's `Given a step` is a
93
+ placeholder, not a spec.
94
+
95
+ ## Anti-patterns, by name
96
+
97
+ - **The grab-bag surface.** One `experience.feature` holding pointer
98
+ capture, i18n fallback, a compiler tag rule, and "the review report
99
+ must not claim video quality". Four surfaces and a process rule. Split
100
+ by what the user meets; move the rule to `lane:process`.
101
+ - **A Then about a document.** "Then the report distinguishes X from Y."
102
+ The product has no report. `lane:process`, or `policy.yaml`.
103
+ - **A Feature title that is a theme.** "Fidelity of the original
104
+ experience" names no surface. Name the screen.
105
+ - **Hand-stamped provenance.** Eighty lines with one timestamp and the
106
+ same `agent_id`. Use `canon scenario new --actor`.
107
+ - **Coverage by splitting.** One behavior as two scenarios so two rows go
108
+ green. One behavior, one id.
109
+ - **Runner tags as classification.** `@design`, `@p2`, `@wip` are
110
+ invisible to canon — they index nothing. Only `@lane:` and `@subject:`
111
+ are read; any other `@name:value` namespace is counted as a diagnostic
112
+ at sync so you can see it was dropped.
113
+
114
+ ## Worked example
115
+
116
+ ```
117
+ specs/features/kind=feature/area=checkout/
118
+ cart.feature # surface: the cart screen
119
+ payment.feature # surface: the payment step
120
+ ```
121
+
122
+ ```gherkin
123
+ Feature: Cart
124
+ # canon: {"schema":1,"at":"2026-09-16T10:02:11Z","actor":{"agent_id":"Main"}}
125
+
126
+ # canon: {"schema":1,"at":"2026-09-16T10:02:11Z","actor":{"agent_id":"Main"}}
127
+ @subject:checkout-core
128
+ @lane:behavior
129
+ @checkout.cart.01
130
+ Scenario: Adding an item raises the count
131
+ Given an empty cart
132
+ When one item is added
133
+ Then the count reads 1
134
+
135
+ # canon: {"schema":1,"at":"2026-09-16T10:02:40Z","actor":{"agent_id":"Main"}}
136
+ @subject:checkout-core
137
+ @lane:design
138
+ @checkout.cart.02
139
+ Scenario: The count badge uses the accent token and a 120ms ease-out
140
+ Given a cart with one item
141
+ When a second item is added
142
+ Then the badge animates over 120ms with ease-out in the accent color
143
+
144
+ # canon: {"schema":1,"at":"2026-09-16T10:03:02Z","actor":{"agent_id":"Main"}}
145
+ @subject:checkout-core
146
+ @lane:process
147
+ @checkout.cart.03
148
+ Scenario: A cart review names the device it was run on
149
+ Given a cart design review
150
+ When the report is written
151
+ Then it names the device and OS the review ran on
152
+ ```
153
+
154
+ `.03` is excluded from coverage by `exclude_lanes: [process]`; `.01` is
155
+ attested by a test, `.02` by a design review.
@@ -0,0 +1,137 @@
1
+ # canon-context
2
+
3
+ `canon context` is a capability QUERY over the same schema/policy/
4
+ vocabulary registry `canon format`/`canon gate` validate against — never
5
+ validation itself. It answers "what CAN I author in this repo, and how?":
6
+ record kinds + their envelope fields, enum domains, join-key grammars,
7
+ partition layout, policy-derived evidence requirements, the typed
8
+ authoring vocabulary, and the CEL binding surface — folded into one
9
+ deterministic authoring surface.
10
+
11
+ ## `canon context [--repo <dir>] [--json]`
12
+
13
+ ```bash
14
+ canon context # compact human outline (prompt-injectable)
15
+ canon context --json # the full machine-readable surface
16
+ canon context --repo ../other # resolve a specific repo's surface
17
+ ```
18
+
19
+ ## Reproducible context packs and prompt bundles
20
+
21
+ Use an explicit, repository-relative JSON manifest when a run needs a
22
+ replayable snapshot. The manifest selects files; Canon copies their bytes into
23
+ an immutable content-addressed registry and records only metadata and digests
24
+ in the pack manifest.
25
+
26
+ ```bash
27
+ canon context-pack create --manifest .canon/context-manifest.json
28
+ canon context-pack create --manifest .canon/context-manifest.json --repo ../my-repo --json
29
+ canon context-pack show sha256:<pack-digest> --repo . --json
30
+ canon context-pack verify sha256:<pack-digest>
31
+ ```
32
+
33
+ `create` prints only the pack id by default, so captured content is never
34
+ written to concise terminal output. `--json` prints the pack manifest,
35
+ including selected-path metadata and content digests. `show` verifies the
36
+ manifest and immutable content objects before returning it; `verify` exits
37
+ nonzero when the pack is missing, stale, or tampered. Manifest paths must be
38
+ relative to the resolved repo, remain inside it, and contain no symlink;
39
+ missing or unsafe paths are rejected by the context-pack module.
40
+
41
+ ## Provider-neutral adapter responses
42
+
43
+ An execution provider may return a versioned response envelope for Canon to
44
+ validate. Validation is read-only: `canon` never starts a provider, interprets
45
+ provider-specific extension payloads, or enforces declared capabilities.
46
+
47
+ ```bash
48
+ canon adapter validate --response response.json
49
+ canon adapter validate --response response.json --repo . --json
50
+ ```
51
+
52
+ The envelope has `protocol_version: 1`, required `run_id`, `provider`,
53
+ `model`, and `context_pack_id`, a closed status vocabulary
54
+ (`succeeded`/`failed`/`aborted`), and strict filesystem/network/secrets
55
+ capability vocabularies. Evidence references are safe, relative references
56
+ with `sha256:<64-hex>` digests. Telemetry timestamps must be RFC3339 with
57
+ `ended_at >= started_at`, and counts/cost are nonnegative.
58
+
59
+ Core fields are closed and unknown fields are rejected. Provider-specific JSON
60
+ belongs only under opaque `extensions`; successful JSON output contains the
61
+ normalized core summary and extension key names, never extension payloads.
62
+ `--repo` can verify the referenced context pack; file-only validation reports
63
+ `context_join_verified: null`. A valid declaration is not a sandbox:
64
+ providers MUST enforce filesystem, network, and secret capabilities before
65
+ execution.
66
+
67
+ Version prompt inputs separately when several runs should share the same
68
+ prompt files:
69
+
70
+ ```bash
71
+ canon prompt register --name reviewer --version v1 \
72
+ --manifest .canon/reviewer-prompt.json
73
+ canon prompt show --name reviewer --version v1 --json
74
+ ```
75
+
76
+ Prompt registration uses the same immutable content registry. `prompt show`
77
+ verifies the selected bundle and returns its metadata/digests; neither concise
78
+ output form includes captured prompt bytes.
79
+
80
+ - **A capability query, never validation:** `canon context` ALWAYS exits
81
+ `0` with the full surface — even when `canon format --check` or
82
+ `canon gate` would report diagnostics against the same repo. It reads
83
+ only the schema/policy/vocabulary registry, never the corpus or the
84
+ evidence ledger.
85
+ - `--repo` resolves through the same nearest-`canon.yaml`-ancestor walk
86
+ `canon format`/`canon gate`/`canon retrieve` use: omitted or `.` walks up
87
+ from cwd to the project root; any other explicit dir is used as-is.
88
+ - `--json` renders the full surface; the default renders a compact per-
89
+ section outline. Both project from the identical resolution (byte-stable
90
+ across runs).
91
+
92
+ ## The surface sections
93
+
94
+ - **`kinds`** — each record kind's `{schema_version, envelope_fields,
95
+ partition}`.
96
+ - **`enums`** — each enum domain's members (verdicts, statuses, lanes,
97
+ roles, polarity, …).
98
+ - **`joinKeys`** — the join-spine grammar string per key (`regime_key`,
99
+ `session_id`, `task_id`, …).
100
+ - **`policy`** — the resolved `policy.yaml`-derived evidence/trust
101
+ requirements per kind.
102
+ - **`vocab`** — the typed authoring vocabulary (directive/enum/evidence-
103
+ kind index) + its content-hash version.
104
+ - **`cel`** — the per-kind CEL binding surface: the bindable
105
+ `record.<field>` names + types and the callable function allowlist a
106
+ `policy.yaml` `applies_when:`/predicate may reference. The agent-facing
107
+ answer to "what can a CEL predicate for this kind reference?"
108
+
109
+ ## Single source of truth
110
+
111
+ `canon context` never re-derives a schema/policy/enum list of its own — it
112
+ resolves through the same shared schema, policy, and vocabulary loaders
113
+ the validators use. A schema edit propagates to `canon context`'s output
114
+ AND `canon format`'s diagnostics from one edit site, never two.
115
+
116
+ ## Reading an "expected one of: …" validator error
117
+
118
+ When `canon format --check` rejects an out-of-domain enum value it emits:
119
+
120
+ ```
121
+ `not-a-real-kind` is not a valid value for `kind` of `run` (expected one of: run, review, divergence, …)
122
+ ```
123
+
124
+ The `expected one of:` member list is the SAME enum domain `canon context
125
+ --json`'s `enums.<field>` reports for that kind — so to fix the artifact,
126
+ pick a member `canon context` lists. Both read the member list off the
127
+ same schema, no hand-maintained second copy.
128
+
129
+ ## What this skill does NOT cover
130
+
131
+ - `canon format --check`'s full violation taxonomy — see `canon-fmt`;
132
+ this skill only covers reading its enum diagnostic against the surface.
133
+ - Authoring a typed task atom / handoff body — see
134
+ `canon-vocab`; `canon context`'s `vocab`/`cel` sections
135
+ tell you what's declarable, not how to declare it.
136
+ - `policy.yaml` CEL authoring semantics — see `canon-policy`; this skill
137
+ only surfaces the bindable identifier/function set per kind.
@@ -0,0 +1,43 @@
1
+ # canon-fmt
2
+
3
+ `canon format --check` is canon's read-only format validator over a
4
+ consumer-repo artifact corpus: ledger records (run/drill/review/clear/
5
+ code-review/design-review), divergence events (manifest/review/
6
+ remediation), `features/`, `inventory/`, and `policy.yaml`. It never
7
+ rewrites or mutates the corpus — it only validates and reports.
8
+
9
+ ## Running
10
+
11
+ ```bash
12
+ canon format --check <corpus-root> # e.g. a consumer repo's spec/
13
+ ```
14
+
15
+ Read-only, always. Exits nonzero if any violation is found, and prints
16
+ a report grouped by failure class in this fixed order:
17
+
18
+ | Failure class | Meaning | Fix |
19
+ |---|---|---|
20
+ | `layout-grammar` | Wrong Hive path shape — a `features/`/`inventory/` file not under a `kind=<kind>/area=<area>/` prefix, a wrong leaf filename, or `assets.lock`'s ad-hoc format. | Move the file under the correct `kind=…/area=…/` prefix with the expected leaf name. |
21
+ | `missing-envelope` | A YAML file (`inventory/`, `policy.yaml`) has no top-level `schema`/`kind`/`at`/`actor` keys. | Add the four envelope keys at the top level. |
22
+ | `missing-provenance` | A `.feature` file's `Feature:`/`Scenario:` header has no `# canon: {...}` comment either directly above it (with its tags, no blank line) or directly below it. | Use `canon scenario new` / `canon feature new`, which stamp it; never hand-type the line. See `canon-authoring`. |
23
+ | `missing-actor` | No structured `actor` object — a bare `by` string, or absent. | Replace `by: <name>` with a structured `actor` object. |
24
+ | `unspecified-evidence` | A ledger `run`/`drill` record's `evidence` field is absent or an empty, untyped array. | Add a typed `evidence` array. |
25
+ | `free-text-ref` | `upstream_ref`/`port_ref` has a segment not matching `<file>#<symbol>[:<a>-<b>]`. | Rewrite the ref into the `<file>#<symbol>[:<a>-<b>]` grammar. |
26
+ | `joined-ref` | `upstream_ref`/`port_ref` is `;`- or `,`-joined but not split into a structured `refs` array. | Split into a `refs:` array, one entry per ref. |
27
+ | `abbreviated-sha` | `app_sha`/`harness_sha` is present but shorter than the full 40-hex form. | Use the full 40-character SHA. |
28
+ | `one-way-backref` | A divergence review event's `ledger_ref` has no reciprocal `divergence_refs` entry on the ledger record it names. | Add the matching `divergence_refs` entry to that ledger record. |
29
+ | `missing-join-identity` | Corpus-wide, one line: no ledger record carries `change_id`/`task_id`. | Standing note only — new records carry these going forward; historical backfill is out of scope. |
30
+
31
+ Every class is a stable string, safe to grep/diff across runs.
32
+
33
+ ## What it does NOT do
34
+
35
+ - **Rewrite anything.** It only validates and reports; a one-shot
36
+ migration of an existing corpus is a separate, per-repo concern.
37
+ - **Backfill `change_id`/`task_id` on historical records.** Both stay
38
+ absent on records that predate the schema; `missing-join-identity` is
39
+ a standing corpus-level note, not a per-record defect it fixes.
40
+ - **Guess a `{file, symbol}` from free prose.** A non-conforming ref is
41
+ reported as `free-text-ref` exactly as found, never fabricated.
42
+ - **Touch a consumer repo's CI gates.** Conforming a corpus and
43
+ reconciling its own tooling is that repo's own operational step.