coderifts 6.0.2 → 7.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,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 7.0.0
4
+
5
+ ### Breaking
6
+
7
+ - **`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.
8
+ - **`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.
9
+ - Thin diff routes (`/api/v1/{grpc,graphql,asyncapi}/diff`) return `422 ANALYSIS_FAILED` on parse errors instead of a generic 500.
10
+
11
+ ### Added
12
+
13
+ - `setup-required-check --bind-workflow` and the provider-enforcement evidence envelope (`provider-enforcement-evidence.v2`).
14
+ - Provider adapter shape with GitLab/Bitbucket read-back (plan only; apply is GitHub).
15
+
16
+ ### Note
17
+
18
+ - Requires `@coderifts/agent-guard` 13 or 14 (declared `^13.0.0`; the CLI uses base guard APIs, not the ATOMIC profile).
19
+
20
+ ## 6.1.0
21
+
22
+ ### Added
23
+
24
+ - **enforce --check now reads GitHub rulesets.** The evaluator existed with zero production call sites: a ruleset-governed repository reported required_check NOT_VERIFIED while its merges were gated. Rulesets are readable where classic protection is not (200 vs 401), so required_check can now reach VERIFIED on an unreadable classic response.
25
+
3
26
  ## 6.0.2
4
27
 
5
28
  ### Fixed
package/README.md CHANGED
@@ -421,6 +421,9 @@ failed, **1** on missing repo/key, API error, or any apply failure.
421
421
  | `--check` | Query GitHub + local workflows (read-only evidence) | `false` |
422
422
  | `--env <name>` | With `--check`: GitHub Environment to query | none |
423
423
  | `--branch <name>` | With `--check`: branch (default: repo default) | default branch |
424
+ | `--provider <name>` | With `--check`: provider adapter. `github` only; others report UNVERIFIABLE and are never stubbed | `github` |
425
+ | `--head-sha <sha>` | With `--check`: pull-request head commit, so the required-workflow pin can be compared with the bytes being merged | none |
426
+ | `--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
427
  | `--json` | Machine-readable JSON result | `false` |
425
428
 
426
429
  ```bash
@@ -460,6 +463,58 @@ VERIFIED): each layer's status, `reason_code`, the API endpoint consulted,
460
463
  `queried_at`. Exit **0** when a report was produced (evidence, not a gate).
461
464
  GitLab/Bitbucket: no adapter — `UNVERIFIABLE`, never stubbed.
462
465
 
466
+ ### `coderifts provider-canary [repo]`
467
+
468
+ Negative canary: open an ephemeral PR with the contract-gate check failing or
469
+ absent and ask GitHub to merge it. **Blocked is the healthy result.** On a
470
+ repository that does **not** block, the canary leaves a merge+revert commit
471
+ pair on base (that is the finding, stated) — an admin token's revert bypasses
472
+ required checks.
473
+
474
+ | Flag | Description | Default |
475
+ |------|-------------|---------|
476
+ | `[repo]` / `--repo <owner/repo>` | Repository | none (required) |
477
+ | `--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 |
478
+ | `--dry-run` | Do not touch the provider; report the shape only | `false` |
479
+ | `--json` | Machine-readable evidence document | `false` |
480
+
481
+ Exit **0** only when the provider refused the merge. A merge that **succeeds**
482
+ is the finding: `blocked:false`, status `NOT_VERIFIED`, exit **1**.
483
+
484
+ ```bash
485
+ coderifts provider-canary --repo owner/repo --dry-run
486
+ coderifts provider-canary --repo owner/repo --confirm
487
+ coderifts provider-canary --repo owner/repo --confirm --json
488
+ ```
489
+
490
+ ### `coderifts grant publish`
491
+
492
+ Encodes an **already-signed** execution grant plus the PR head sha into a
493
+ comment marker on stdout, for the 1094 PR-comment delivery slot. **It does not
494
+ mint a grant** (`src/commands/grant-publish.js:4`) — the token must already be a
495
+ signed `cr.exec.v1` or `cr.exec.v2`.
496
+
497
+ | Flag | Description | Default |
498
+ |------|-------------|---------|
499
+ | `--head-sha <sha>` | **Required.** 40-hex PR head sha the grant binds | none |
500
+ | `--grant <token>` | **Required.** `cr.exec.v1` or `cr.exec.v2` token | none |
501
+ | `--after-payload-hash <hash>` | `sha256:…` of the after-payload | empty string |
502
+
503
+ Writes `<!-- coderifts-grant-v2 -->` followed by one JSON line. Pipe it to
504
+ `gh api` or post it as a PR comment. Bad `--head-sha` or a missing `--grant`
505
+ exits **2** with usage.
506
+
507
+ ```bash
508
+ coderifts grant publish --head-sha "$HEAD" --grant "$TOKEN" > /tmp/marker.txt
509
+ ```
510
+
511
+ **Reaching the gate today:** the contract-gate Action reads a grant from its
512
+ `execution-grant:` input, not from PR comments — `main()` in the gate does not
513
+ pass `grantComments`, so the comment path is not yet wired end to end. Until it
514
+ is, deliver the token through the `CODERIFTS_EXECUTION_GRANT` repository
515
+ variable, and note that a grant binds one after-payload, so a pinned variable
516
+ covers exactly one diff.
517
+
463
518
  ### Agent host files (`coderifts agent-setup`)
464
519
 
465
520
  Writes the six CodeRifts agent-host rule files **plus** the Claude Code
@@ -534,6 +589,11 @@ via `coderifts registry-gate`). The wizard:
534
589
  existing protection settings
535
590
  5. **`--apply`:** runs that PUT with your credentials, then **re-reads** and
536
591
  only reports success if the check is required
592
+ 6. **`--bind-workflow`:** create/update a repository ruleset `workflows` rule
593
+ pinning `.github/workflows/coderifts.yml` (the STRICT template filename)
594
+ to its current **blob sha** on the target branch (`enforcement: active`,
595
+ `bypass_actors: []`). Dry-run unless combined with `--apply`. Read-back
596
+ uses `github-workflow-pin.js`; reports `APPLIED` only on `VERIFIED`.
537
597
 
538
598
  **Why the App never writes protection:** mutating branch protection needs
539
599
  `administration:write` — a trust jump we refuse. Your credentials do the write.
@@ -542,6 +602,15 @@ via `coderifts registry-gate`). The wizard:
542
602
  `merge_group` webhook (`checks_requested`) and posts **CodeRifts / contract-gate**
543
603
  on the merge group head (same gate as PR heads), so the queue does not stall.
544
604
 
605
+ | Flag | Description | Default |
606
+ |------|-------------|---------|
607
+ | `--repo <owner/repo>` | Override owner/repo | `git remote origin` |
608
+ | `--branch <name>` | Branch to protect | repo default branch |
609
+ | `--apply` | Apply the protection change | print the `gh` command only |
610
+ | `--bind-workflow` | Create/update a ruleset `workflows` rule pinning `.github/workflows/coderifts.yml` (STRICT template) by blob sha | `false` |
611
+ | `--enforce-admins` | Set `enforce_admins:true` on create | `false` — admins can bypass; the gate reports `admin_bypass_open` |
612
+ | `--json` | Machine-readable JSON result | `false` |
613
+
545
614
  ```bash
546
615
  # Observe + print command (safe default)
547
616
  coderifts setup-required-check
@@ -549,6 +618,12 @@ coderifts setup-required-check
549
618
  # Apply + re-verify
550
619
  coderifts setup-required-check --apply
551
620
 
621
+ # Pin the STRICT workflow by blob sha (ruleset `workflows` rule)
622
+ coderifts setup-required-check --bind-workflow --apply
623
+
624
+ # Admins cannot bypass either (opt-in; NOT the default)
625
+ coderifts setup-required-check --apply --enforce-admins
626
+
552
627
  # One-liner for CI docs
553
628
  coderifts setup-required-check --branch main
554
629
  ```
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');