coderifts 6.1.0 → 8.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 8.0.0
4
+
5
+ ### Security
6
+
7
+ - **Installs from `coderifts@7.0.0` pinned contract-gate v0.5.0.** That action's vendored verifier accepted receipts signed by revoked keys. Fixed in contract-gate **0.7.0**. The strict install now pins **0.7.0 / `90de4be`**.
8
+
9
+ ### Breaking
10
+
11
+ - **`@coderifts/agent-guard` declared `^15.0.0`** (`MIN_GUARD_MAJOR` 15). There is no `peerDependencies` entry — this is a **direct** dependency, so a fresh `npm i -g coderifts` resolves 15.x, not 14.x. The CLI still uses only `deployGate`, `asVerifiedDeployReceiptView`, `DEPLOY_RECEIPT_VIEW_SPEC`, and `matchGlob`.
12
+
13
+ ### Fixed
14
+
15
+ - **Claude Code PreToolUse hook governs a path family**, not a single `coderifts.specPath`. Default globs cover OpenAPI/Swagger, GraphQL, `*.proto`, AsyncAPI, MCP manifests, and `*tool-schema*.json`. `git config --add coderifts.specPath` is additive (never drops the family). `coderifts.specPathExclude` opts one path out. A not-governed Write/Edit/MultiEdit exits 0 and prints `coderifts claude-hook: not governed: <path>` — the hook is never silent on those tools.
16
+
17
+ ### Docs
18
+
19
+ - README **Grant versions** table: `cr.exec.v1` is the reference/teaching grant the demo and strict install use; the Atomic V2 request contract (`execution-grant-request.v2` fields) is what the SDK/Guard/executor speak; `ENFORCING_STRICT` is the current profile name and will become a versioned name.
20
+
21
+ ## 7.0.0
22
+
23
+ ### Breaking
24
+
25
+ - **`provider-canary`** — proves the gate BLOCKS by attempting a merge that must be refused, creating and cleaning up a real PR; on a non-blocking repo it reverts the probe commit in the same run (`base_reverted`). A successful merge yields `NOT_VERIFIED` and exit 1 — the honest finding, not a bug.
26
+ - **`enforce --check`** gains `--bind-workflow` (writes and reads back the SHA-pinned required-workflow rule), `--require-workflow-binding`, `--head-sha`, and `--env`; runtime read-back covers the rule-suite result, check-run app id, and check-run head_sha. Status vocabulary is VERIFIED / NOT_VERIFIED / UNVERIFIABLE.
27
+ - Thin diff routes (`/api/v1/{grpc,graphql,asyncapi}/diff`) return `422 ANALYSIS_FAILED` on parse errors instead of a generic 500.
28
+
29
+ ### Added
30
+
31
+ - `setup-required-check --bind-workflow` and the provider-enforcement evidence envelope (`provider-enforcement-evidence.v2`).
32
+ - Provider adapter shape with GitLab/Bitbucket read-back (plan only; apply is GitHub).
33
+
34
+ ### Note
35
+
36
+ - Requires `@coderifts/agent-guard` 13 or 14 (declared `^13.0.0`; the CLI uses base guard APIs, not the ATOMIC profile).
37
+
3
38
  ## 6.1.0
4
39
 
5
40
  ### Added
package/README.md CHANGED
@@ -86,6 +86,16 @@ coderifts init --agents --check # which of the four pieces are present
86
86
  | **Host hook** | `.claude/settings.json`, `.cursor/hooks.json` | Reuses `hook install --claude` / `--cursor` (JSON-merge, backup, idempotent). Copilot shares Claude's `.claude/settings.json` — VS Code reads it by default. |
87
87
  | **CI workflow** | `.github/workflows/coderifts.yml` | `coderifts/contract-gate@v0`. Default: `require-verified-monitoring` commented (honest default: **false**). `--strict`: the same file with `require-verified-monitoring: 'true'` |
88
88
 
89
+ **Grant versions** (these names are not interchangeable):
90
+
91
+ | What | Who uses it | What it proves |
92
+ |------|-------------|----------------|
93
+ | `cr.exec.v1` | capability-demo and the strict-install workflow (`require-grant`) | A reference/teaching execution grant bound to `operation ∥ target_id ∥ after_payload`. The demo and `coderifts init --agents --strict` use this form. |
94
+ | Atomic V2 request (`execution-grant-request.v2` fields) | SDK, Guard, executor | The request contract those three speak when minting/verifying a grant. Not the same bytes as a `cr.exec.v1` token. |
95
+ | `ENFORCING_STRICT` | Guard `withCodeRifts({ profile })` | The current **profile name**. It is unsuffixed today and will become a versioned name; it is not a grant format and not Atomic V1. |
96
+
97
+ `--strict` turns on `require-grant` and `require-verified-monitoring` in the workflow; it does not rename those three things.
98
+
89
99
  **Next manual step:** add `CODERIFTS_API_KEY` to the repository secrets. The
90
100
  CLI never writes a key anywhere.
91
101
 
@@ -287,7 +297,26 @@ Unparseable settings JSON → refuse, no write.
287
297
  ### Claude Code PreToolUse (`coderifts claude-hook`) — ID824
288
298
 
289
299
  Tool-call-time gate for **Claude Code**: blocks contract-touching `Write` /
290
- `Edit` / `MultiEdit` when authorize preflight returns **BLOCK/STOP**.
300
+ `Edit` / `MultiEdit` when authorize preflight returns **BLOCK/STOP**. The
301
+ matcher is tool-level (`Write|Edit|MultiEdit`); the **path family** is
302
+ enforced inside the hook.
303
+
304
+ **Default governed family** (from `SUPPORTED_TYPES` + `@coderifts/contract-path`
305
+ `looksLikeContractPath` / `CONTRACT_EXT`; `agent_tools` via `*tool-schema*.json`):
306
+
307
+ - `**/*openapi*.{yaml,yml,json}` and `**/*swagger*.{yaml,yml,json}` (openapi)
308
+ - `**/*.{graphql,gql}` (graphql)
309
+ - `**/*.proto` (grpc)
310
+ - `**/*asyncapi*.{yaml,yml,json}` (asyncapi)
311
+ - `**/*mcp*.{json,yaml,yml}` (mcp_manifest)
312
+ - `**/*tool-schema*.json` (agent_tools)
313
+
314
+ `node_modules/` and `vendor/` are not governed (same as the path detector).
315
+ `git config --add coderifts.specPath <path-or-glob>` is **additive** (never
316
+ replaces the family). `git config --add coderifts.specPathExclude <path-or-glob>`
317
+ opts one path out. A not-governed path exits 0 and prints
318
+ `coderifts claude-hook: not governed: <path>` on stderr — the hook is never silent
319
+ on a write/edit.
291
320
 
292
321
  **Exit map (Claude Code semantics — fixed):**
293
322
 
@@ -299,11 +328,12 @@ Tool-call-time gate for **Claude Code**: blocks contract-touching `Write` /
299
328
 
300
329
  Push-time equivalent (git exit **1** on BLOCK): `coderifts hook install`.
301
330
 
302
- **Install path:** `coderifts hook install --claude` (or `agent-setup` writes project `.claude/settings.json` when absent). Then set key + spec:
331
+ **Install path:** `coderifts hook install --claude` (or `agent-setup` writes project `.claude/settings.json` when absent). Then set key; spec path is optional and additive:
303
332
 
304
333
  ```bash
305
334
  git config coderifts.apiKey 'cr_live_…' # or: coderifts login / CODERIFTS_API_KEY
306
- git config coderifts.specPath api/openapi.yaml # default if omitted
335
+ git config --add coderifts.specPath extra/spec.yaml # additive; does not drop the family
336
+ git config --add coderifts.specPathExclude docs/openapi.yaml # opt out one false positive
307
337
  ```
308
338
 
309
339
  **Recipe — project** (`.claude/settings.json`) **or user** (`~/.claude/settings.json`):
@@ -421,6 +451,9 @@ failed, **1** on missing repo/key, API error, or any apply failure.
421
451
  | `--check` | Query GitHub + local workflows (read-only evidence) | `false` |
422
452
  | `--env <name>` | With `--check`: GitHub Environment to query | none |
423
453
  | `--branch <name>` | With `--check`: branch (default: repo default) | default branch |
454
+ | `--provider <name>` | With `--check`: provider adapter. `github` only; others report UNVERIFIABLE and are never stubbed | `github` |
455
+ | `--head-sha <sha>` | With `--check`: pull-request head commit, so the required-workflow pin can be compared with the bytes being merged | none |
456
+ | `--require-workflow-binding` | With `--check`: also require a sha-pinned required workflow matching the head. **Default OFF** — without it the evidence still gains a `workflow_binding` section and neither the verdict nor the exit code changes | `false` |
424
457
  | `--json` | Machine-readable JSON result | `false` |
425
458
 
426
459
  ```bash
@@ -460,6 +493,58 @@ VERIFIED): each layer's status, `reason_code`, the API endpoint consulted,
460
493
  `queried_at`. Exit **0** when a report was produced (evidence, not a gate).
461
494
  GitLab/Bitbucket: no adapter — `UNVERIFIABLE`, never stubbed.
462
495
 
496
+ ### `coderifts provider-canary [repo]`
497
+
498
+ Negative canary: open an ephemeral PR with the contract-gate check failing or
499
+ absent and ask GitHub to merge it. **Blocked is the healthy result.** On a
500
+ repository that does **not** block, the canary leaves a merge+revert commit
501
+ pair on base (that is the finding, stated) — an admin token's revert bypasses
502
+ required checks.
503
+
504
+ | Flag | Description | Default |
505
+ |------|-------------|---------|
506
+ | `[repo]` / `--repo <owner/repo>` | Repository | none (required) |
507
+ | `--confirm` | Required for a real run: this creates a branch and a pull request | none — without it (and without `--dry-run`) the command exits **2** rather than touching the repo |
508
+ | `--dry-run` | Do not touch the provider; report the shape only | `false` |
509
+ | `--json` | Machine-readable evidence document | `false` |
510
+
511
+ Exit **0** only when the provider refused the merge. A merge that **succeeds**
512
+ is the finding: `blocked:false`, status `NOT_VERIFIED`, exit **1**.
513
+
514
+ ```bash
515
+ coderifts provider-canary --repo owner/repo --dry-run
516
+ coderifts provider-canary --repo owner/repo --confirm
517
+ coderifts provider-canary --repo owner/repo --confirm --json
518
+ ```
519
+
520
+ ### `coderifts grant publish`
521
+
522
+ Encodes an **already-signed** execution grant plus the PR head sha into a
523
+ comment marker on stdout, for the 1094 PR-comment delivery slot. **It does not
524
+ mint a grant** (`src/commands/grant-publish.js:4`) — the token must already be a
525
+ signed `cr.exec.v1` or `cr.exec.v2` (see **Grant versions** above).
526
+
527
+ | Flag | Description | Default |
528
+ |------|-------------|---------|
529
+ | `--head-sha <sha>` | **Required.** 40-hex PR head sha the grant binds | none |
530
+ | `--grant <token>` | **Required.** `cr.exec.v1` or `cr.exec.v2` token | none |
531
+ | `--after-payload-hash <hash>` | `sha256:…` of the after-payload | empty string |
532
+
533
+ Writes `<!-- coderifts-grant-v2 -->` followed by one JSON line. Pipe it to
534
+ `gh api` or post it as a PR comment. Bad `--head-sha` or a missing `--grant`
535
+ exits **2** with usage.
536
+
537
+ ```bash
538
+ coderifts grant publish --head-sha "$HEAD" --grant "$TOKEN" > /tmp/marker.txt
539
+ ```
540
+
541
+ **Reaching the gate today:** the contract-gate Action reads a grant from its
542
+ `execution-grant:` input, not from PR comments — `main()` in the gate does not
543
+ pass `grantComments`, so the comment path is not yet wired end to end. Until it
544
+ is, deliver the token through the `CODERIFTS_EXECUTION_GRANT` repository
545
+ variable, and note that a grant binds one after-payload, so a pinned variable
546
+ covers exactly one diff.
547
+
463
548
  ### Agent host files (`coderifts agent-setup`)
464
549
 
465
550
  Writes the six CodeRifts agent-host rule files **plus** the Claude Code
@@ -534,6 +619,11 @@ via `coderifts registry-gate`). The wizard:
534
619
  existing protection settings
535
620
  5. **`--apply`:** runs that PUT with your credentials, then **re-reads** and
536
621
  only reports success if the check is required
622
+ 6. **`--bind-workflow`:** create/update a repository ruleset `workflows` rule
623
+ pinning `.github/workflows/coderifts.yml` (the STRICT template filename)
624
+ to its current **blob sha** on the target branch (`enforcement: active`,
625
+ `bypass_actors: []`). Dry-run unless combined with `--apply`. Read-back
626
+ uses `github-workflow-pin.js`; reports `APPLIED` only on `VERIFIED`.
537
627
 
538
628
  **Why the App never writes protection:** mutating branch protection needs
539
629
  `administration:write` — a trust jump we refuse. Your credentials do the write.
@@ -542,6 +632,15 @@ via `coderifts registry-gate`). The wizard:
542
632
  `merge_group` webhook (`checks_requested`) and posts **CodeRifts / contract-gate**
543
633
  on the merge group head (same gate as PR heads), so the queue does not stall.
544
634
 
635
+ | Flag | Description | Default |
636
+ |------|-------------|---------|
637
+ | `--repo <owner/repo>` | Override owner/repo | `git remote origin` |
638
+ | `--branch <name>` | Branch to protect | repo default branch |
639
+ | `--apply` | Apply the protection change | print the `gh` command only |
640
+ | `--bind-workflow` | Create/update a ruleset `workflows` rule pinning `.github/workflows/coderifts.yml` (STRICT template) by blob sha | `false` |
641
+ | `--enforce-admins` | Set `enforce_admins:true` on create | `false` — admins can bypass; the gate reports `admin_bypass_open` |
642
+ | `--json` | Machine-readable JSON result | `false` |
643
+
545
644
  ```bash
546
645
  # Observe + print command (safe default)
547
646
  coderifts setup-required-check
@@ -549,6 +648,12 @@ coderifts setup-required-check
549
648
  # Apply + re-verify
550
649
  coderifts setup-required-check --apply
551
650
 
651
+ # Pin the STRICT workflow by blob sha (ruleset `workflows` rule)
652
+ coderifts setup-required-check --bind-workflow --apply
653
+
654
+ # Admins cannot bypass either (opt-in; NOT the default)
655
+ coderifts setup-required-check --apply --enforce-admins
656
+
552
657
  # One-liner for CI docs
553
658
  coderifts setup-required-check --branch main
554
659
  ```
package/bin/coderifts.js CHANGED
@@ -85,6 +85,60 @@ program
85
85
  process.exit(code);
86
86
  });
87
87
 
88
+ // ── provider canary (1105 negative enforcement proof) ──
89
+ //
90
+ // NOT read-only, and deliberately not hidden behind --apply: it creates a branch and a pull request
91
+ // on the named repository and asks the provider to merge them. --dry-run is the safe default the
92
+ // tests drive; a real run needs --confirm so nobody discovers this command by tab-completion.
93
+ program
94
+ .command('provider-canary [repo]')
95
+ .description('Prove the gate BLOCKS by attempting a merge that must be refused (creates + cleans up a real PR). On a non-blocking repo the canary leaves a merge+revert commit pair on base (the finding); an admin token\'s revert bypasses required checks.')
96
+ .option('--repo <owner/repo>', 'Repository (owner/repo); also accepted as a positional argument')
97
+ .option('--dry-run', 'Do not touch the provider; report the shape only', false)
98
+ .option('--confirm', 'Required for a real run: this creates a branch and a pull request')
99
+ .option('--json', 'Machine-readable evidence document')
100
+ .action(async (repoPositional, options) => {
101
+ const { runProviderCanary } = require('../src/commands/provider-canary');
102
+ if (!options.dryRun && !options.confirm) {
103
+ console.error(
104
+ 'provider-canary creates a real branch and pull request on the target repository and then '
105
+ + 'asks GitHub to merge it. Re-run with --confirm, or with --dry-run to see the shape.',
106
+ );
107
+ process.exit(2);
108
+ }
109
+ const result = await runProviderCanary(
110
+ { ...options, repo: options.repo || repoPositional },
111
+ {},
112
+ );
113
+ // The command prints (JSON or human) exactly once, after cleanup — see report() there.
114
+ process.exit(result.exitCode);
115
+ });
116
+
117
+ // ── grant publish (1094 PR-comment delivery) ──
118
+ program
119
+ .command('grant')
120
+ .description('Execution-grant helpers')
121
+ .command('publish')
122
+ .description('Encode a signed grant + head_sha as a PR-comment marker (stdout)')
123
+ .requiredOption('--head-sha <sha>', '40-hex PR head SHA the grant binds')
124
+ .requiredOption('--grant <token>', 'cr.exec.v1 or cr.exec.v2 token')
125
+ .option('--after-payload-hash <hash>', 'sha256:… of the after-payload')
126
+ .action((options) => {
127
+ const { runGrantPublish } = require('../src/commands/grant-publish');
128
+ const result = runGrantPublish({
129
+ headSha: options.headSha,
130
+ grant: options.grant,
131
+ afterPayloadHash: options.afterPayloadHash,
132
+ });
133
+ if (!result.ok) {
134
+ console.error(result.error);
135
+ if (result.usage) console.error(result.usage);
136
+ process.exitCode = result.exitCode;
137
+ process.exit(result.exitCode);
138
+ }
139
+ process.stdout.write(result.text + '\n');
140
+ });
141
+
88
142
  // ── init command ──
89
143
  // `init [template]` writes a policy YAML. `init --agents` is a different path:
90
144
  // it wires MCP config + agent rules + host hook + the CI gate workflow.
@@ -153,6 +207,7 @@ program
153
207
  .option('--branch <name>', 'Branch to protect (default: repo default branch)')
154
208
  .option('--repo <owner/repo>', 'Override owner/repo (default: git remote origin)')
155
209
  .option('--apply', 'Apply the protection change (default: print the exact gh command only)')
210
+ .option('--bind-workflow', 'Create/update a ruleset workflows rule pinning .github/workflows/coderifts.yml (STRICT template) by blob sha; dry-run unless --apply')
156
211
  .option('--enforce-admins', 'Set enforce_admins:true on create (default: false — admins can bypass; gate reports admin_bypass_open)')
157
212
  .option('--json', 'Machine-readable JSON result')
158
213
  .action(async (options) => {
@@ -193,6 +248,11 @@ program
193
248
  .option('--env <name>', 'With --check: GitHub Environment name to query for protection rules')
194
249
  .option('--branch <name>', 'With --check: branch to query (default: repository default branch)')
195
250
  .option('--provider <name>', 'With --check: provider adapter (github only; others UNVERIFIABLE, never stubbed)', 'github')
251
+ // 1102 — DEFAULT OFF ON PURPOSE. With the flag absent the evidence gains a workflow_binding
252
+ // section and NOTHING about the verdict or the exit code changes. Turning it on is a decision
253
+ // about how strong a claim this repository is willing to make, not a bug fix, so it is Peter's.
254
+ .option('--require-workflow-binding', 'With --check: also require a sha-pinned required workflow matching the head (default: off)')
255
+ .option('--head-sha <sha>', 'With --check: pull-request head commit, so the workflow pin can be compared with the bytes being merged')
196
256
  .option('--json', 'Machine-readable JSON result')
197
257
  .action(async (repoPositional, options) => {
198
258
  const { runEnforce } = require('../src/commands/enforce');