@journeykit/canon 0.10.0 → 0.11.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.
@@ -104,6 +104,54 @@ Malformed rows in those three kinds do NOT surface as
104
104
  `malformed-evidence`; they are kept off the evidence violation set on
105
105
  purpose, so enabling nothing changes no existing verdict.
106
106
 
107
+ ## Experimental: evidence binding (`experimental.evidence_binding`, off by default)
108
+
109
+ An evidence record is an attestation: canon never runs `--ref`, and it
110
+ never will — it cannot run every language and every agent-driven QA
111
+ tool a team uses. Binding lets a record point at files the team's own
112
+ runner or agent ALREADY produced, so a reviewer (and the gate) can check
113
+ what backed the claim:
114
+
115
+ ```bash
116
+ # any file: a Playwright trace, screenshots, an agent QA log — by sha256
117
+ canon evidence add --scenario-id cart.add.04 --project-id root \
118
+ --kind agent-qa --ref "stagehand run 7" --role implementer \
119
+ --artifact test-results/cart-trace.zip
120
+ # a JUnit XML or Cucumber JSON report: also records the matched case + outcome
121
+ canon evidence add ... --report junit:target/nextest/default/junit.xml
122
+ canon evidence add ... --report cucumber:reports/cucumber.json
123
+ ```
124
+
125
+ The case is found by the scenario id in its name, classname, or Cucumber
126
+ tag — dotted (`cart.add.04`) or underscored (`cart_add_04`, the form a
127
+ test function carries) — or named exactly with `--report-case <name>`
128
+ (a Rust test by its function name). No match refuses (exit 2); a
129
+ `faithful` verdict over a failed or skipped case refuses (exit 1). Files
130
+ must be inside the repository and are recorded by relative path.
131
+
132
+ Strength, weakest first: `attested` (no attachment) < `artifact` (any
133
+ bound file) < `report` (a parsed report whose case passed). The policy
134
+ decides whether the gate cares:
135
+
136
+ ```yaml
137
+ experimental:
138
+ evidence_binding:
139
+ mode: warn # off (default) | warn | require
140
+ strength: artifact # artifact (default) | report
141
+ case: [failure] # optional filters — only these scenarios are held
142
+ lane: [behavior]
143
+ scope: [verifying] # subject statuses
144
+ ```
145
+
146
+ `off` checks nothing. `warn` prints the distribution and lists each
147
+ scenario below `strength` as `warn <id>` after the gate result, never
148
+ failing it. `require` reports each as `uncovered-cell`. The latest
149
+ evidence record per scenario decides; scenarios with no evidence are
150
+ `spec_coverage`'s finding, not this one's. A malformed section is a
151
+ violation in every mode. Canon still cannot know whether the bound test
152
+ actually exercises the scenario — binding narrows the trust gap, it does
153
+ not close it.
154
+
107
155
  ## Effect-aware risk approvals (`risk_tiers`)
108
156
 
109
157
  `risk_tiers` is absent by default and remains a no-op when absent. When it
@@ -121,11 +169,14 @@ binding. This is still attestation input, not proof of the changed paths;
121
169
  run/diff metadata is authoritative when available, and an unavailable
122
170
  binding is a gate violation rather than a clean result.
123
171
 
124
- Approval identity is separate from attestation text. `--approval-by` is
125
- accepted as a verified approval only when it equals a non-empty
126
- `CANON_ACTOR` environment identity and `--approval-role human` is supplied.
127
- The record persists that verification bit for replay; arbitrary caller
128
- strings, empty identities, and `agent` roles never satisfy a risk tier.
172
+ Approval identity is separate from attestation text. An approval counts
173
+ only when it carries a detached SSH signature (`ssh-keygen -Y sign -n
174
+ canon-approval-v1` over `canon evidence approval-payload`'s bytes) that
175
+ verifies against the signers pinned by policy `approval.allowed_signers`,
176
+ with `--approval-role human`. `CANON_ACTOR`, caller-supplied strings, and
177
+ `agent` roles never satisfy a risk tier, and canon never signs on a
178
+ user's behalf.
179
+
129
180
  Malformed `risk_tiers` sections produce a stable `uncovered-cell` policy
130
181
  violation (`risk_tiers policy-invalid: ...`) instead of degrading to an
131
182
  empty clean map.
@@ -113,7 +113,16 @@ not proof that the caller's path is authoritative; configured rules cannot
113
113
  be bypassed by declaring a safe ref.
114
114
 
115
115
  Approval text is also only an attestation unless identity is verified.
116
- `canon evidence add` marks an approval verified only when `--approval-by`
117
- equals non-empty `CANON_ACTOR` and `--approval-role human` is supplied.
118
- The persisted verification bit is what later gates count; arbitrary caller
119
- strings, empty identities, and agent roles do not count.
116
+ An approval counts only with a detached SSH signature that verifies
117
+ against the signers pinned by `approval.allowed_signers`, and
118
+ `--approval-role human`. `CANON_ACTOR`, arbitrary caller strings, and
119
+ agent roles do not count.
120
+
121
+ ## Experimental: `experimental.evidence_binding`
122
+
123
+ Off by default. Holds scenario evidence to a minimum strength —
124
+ `artifact` (a file bound by `canon evidence add --artifact`) or `report`
125
+ (a JUnit/Cucumber report whose matched case passed, `--report`) — in
126
+ `mode: warn` (advisories only) or `mode: require` (gate violations),
127
+ optionally narrowed by `case`, `lane`, and subject-status `scope`. See
128
+ `canon-gate` for the full contract.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@journeykit/canon",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "canon — harness knowledge substrate. JS launcher: platform detection + native binary resolution.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,9 +30,9 @@
30
30
  },
31
31
  "dependencies": {},
32
32
  "optionalDependencies": {
33
- "@journeykit/canon-core-darwin-arm64": "0.10.0",
34
- "@journeykit/canon-core-linux-x64": "0.10.0",
35
- "@journeykit/canon-core-win32-x64": "0.10.0"
33
+ "@journeykit/canon-core-darwin-arm64": "0.11.0",
34
+ "@journeykit/canon-core-linux-x64": "0.11.0",
35
+ "@journeykit/canon-core-win32-x64": "0.11.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^20.0.0",