coderifts 4.7.0 → 4.9.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,69 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.9.0
4
+
5
+ ### Fixed
6
+ - **P0 / ID621 — `enforce --check` no longer reports `inescapable_deploy: true`
7
+ for an unenforced configuration.** External audit 2026-08-24 reproduced two
8
+ holes on the published CLI: (a) the required check matched by **name only**
9
+ (`required_status_checks.checks[]` with no `app_id`), so any repository
10
+ writer could satisfy it with a same-named success status; (b) the two local
11
+ workflow checks were **text searches**, so `# uses: coderifts/contract-gate@v0`
12
+ in a comment counted as VERIFIED. Both now fail closed.
13
+
14
+ ### Changed
15
+ - **`--json` spec `provider-enforcement-evidence.v2`.** The meaning of
16
+ `VERIFIED` changed. A layer whose evidence cannot be bound to an issuer
17
+ (CodeRifts GitHub App id, public `GET /apps/coderifts`) or to an **active**
18
+ workflow job is `NOT_VERIFIED` or `UNVERIFIABLE` — never `VERIFIED`.
19
+ `inescapable_deploy` is true only when every layer is VERIFIED under these
20
+ rules.
21
+ - **Issuer binding:** `required_check` is VERIFIED only when `checks[]`
22
+ carries our App's numeric `app_id`. Legacy `contexts: string[]` is
23
+ `legacy_contexts_no_issuer_binding`. Name-only `checks[]` is
24
+ `no_issuer_binding`. A different `app_id` is `issuer_app_id_mismatch`.
25
+ - **YAML AST, not grep:** workflow files are parsed with `js-yaml` (already a
26
+ CLI dependency — no new package). VERIFIED requires a job that is not
27
+ `if: false` and a step whose `uses:` is `coderifts/contract-gate` at a
28
+ pinned major, or a `run:` that actually invokes `coderifts deploy-gate`.
29
+ Comments, string literals in other keys, and commented-out steps fail.
30
+ - **Environment correlation:** `environment_protection` is VERIFIED only when
31
+ `--env` names a protected GitHub Environment **and** an active deploy-gate
32
+ job sets that same `environment:` key (`environment_mismatch` otherwise).
33
+
34
+ ## 4.8.0
35
+
36
+ ### Fixed
37
+ - **VS Code / Copilot PreToolUse camelCase (DRIFT 1).** VS Code Agent hooks
38
+ pass `toolName` / `toolInput` / `filePath` (and `targetFile`). The hook
39
+ already tolerated `toolName`/`toolInput` but missed several property
40
+ aliases, so flattened camelCase payloads and `targetFile` were refused as
41
+ unparseable (fail-closed, safe, unusable). Every Claude-shaped read site
42
+ now accepts snake_case and camelCase: `tool_name`/`toolName`,
43
+ `tool_input`/`toolInput`, `file_path`/`filePath`/`path`/`target_file`/`targetFile`,
44
+ `content`/`contents`. Snake_case behaviour is unchanged. Garbage is still
45
+ fail-closed.
46
+
47
+ ### Changed
48
+ - **Matcher-inert hosts (DRIFT 2, documented, not a host fix).** VS Code
49
+ parses hook matchers but does not apply them — the hook fires on every
50
+ tool call. Non-Write/Edit/MultiEdit tools now exit **0** silently with no
51
+ network and **without requiring an API key** (parse → skip → key, so a
52
+ missing key cannot brick Read/Bash). Non-contract Write paths skip before
53
+ the key gate too. Garbage is still fail-closed (`stdin_unparseable`).
54
+ Spec-path Write/Edit/MultiEdit without a key is still fail-closed.
55
+
56
+ ### Added
57
+ - **`copilot` MCP host (DRIFT 3).** `MCP_HOSTS` grows a third entry:
58
+ `.vscode/mcp.json`, root key **`servers`** (not `mcpServers`),
59
+ `type: "http"` + `url`. The writer special-cases the root key; Claude and
60
+ Cursor stay on `mcpServers`. `coderifts init --agents --hosts` accepts
61
+ `copilot` and includes it in `all`. Merge preserves other `servers` (and
62
+ any leftover keys). `--check` uses the same predicate.
63
+ - README Copilot section: `hook install --claude` arms VS Code agent mode
64
+ (measured), matcher-inert behaviour, honest boundary (hooks gate the
65
+ agent, not the human; inline completions are not tool calls).
66
+
3
67
  ## 4.7.0
4
68
 
5
69
  ### Added
package/README.md CHANGED
@@ -72,6 +72,7 @@ coderifts init --agents # all hosts, all four pieces
72
72
  coderifts init --agents --dry-run # plan only — writes nothing
73
73
  coderifts init --agents --hosts=claude # Claude Code only
74
74
  coderifts init --agents --hosts=cursor # Cursor only
75
+ coderifts init --agents --hosts=copilot # GitHub Copilot / VS Code only
75
76
  coderifts init --agents --no-hook # skip host hook install
76
77
  coderifts init --agents --no-workflow # skip .github/workflows/coderifts.yml
77
78
  coderifts init --agents --check # which of the four pieces are present
@@ -79,9 +80,9 @@ coderifts init --agents --check # which of the four pieces are present
79
80
 
80
81
  | Piece | What it writes | Host-specific shape |
81
82
  |-------|----------------|---------------------|
82
- | **MCP config** | `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor) | Claude: `mcpServers.coderifts` with `type: "http"` + `url`. Cursor: `url` only (no `type`). Never `httpUrl` (that is Gemini, extension-level — this command does not write it). |
83
- | **Agent rules** | generated host files via `agent-setup`'s writer | `AGENTS.md` is always written. Claude also gets `CLAUDE.md`; Cursor also gets `.cursor/rules/coderifts.mdc`. `--hosts=all` adds Copilot, LangGraph, and OpenAI instructions. (`coderifts init agent` is a different command — it writes a `.coderifts.yml` template.) |
84
- | **Host hook** | `.claude/settings.json`, `.cursor/hooks.json` | Reuses `hook install --claude` / `--cursor` (JSON-merge, backup, idempotent) |
83
+ | **MCP config** | `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor), `.vscode/mcp.json` (Copilot / VS Code) | Three shapes, never normalised into one: Claude `mcpServers` + `type: "http"` + `url`; Cursor `mcpServers` + `url` only (no `type`); Copilot **`servers`** (not `mcpServers`) + `type: "http"` + `url`. Never `httpUrl` (that is Gemini, extension-level — this command does not write it). |
84
+ | **Agent rules** | generated host files via `agent-setup`'s writer | `AGENTS.md` is always written. Claude also gets `CLAUDE.md`; Cursor also gets `.cursor/rules/coderifts.mdc`; Copilot also gets `.github/copilot-instructions.md`. `--hosts=all` adds LangGraph and OpenAI instructions. (`coderifts init agent` is a different command — it writes a `.coderifts.yml` template.) |
85
+ | **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. |
85
86
  | **CI workflow** | `.github/workflows/coderifts.yml` | `coderifts/contract-gate@v0`. `require-verified-monitoring` is commented (honest default: **false**) |
86
87
 
87
88
  **Next manual step:** add `CODERIFTS_API_KEY` to the repository secrets. The
@@ -347,6 +348,37 @@ weakened by `CODERIFTS_ADVISORY`.
347
348
  CODERIFTS_ADVISORY=1 coderifts claude-hook
348
349
  ```
349
350
 
351
+ ### GitHub Copilot / VS Code agent mode
352
+
353
+ Measured (VS Code **1.110**, Agent hooks Preview, 2026-08-24): VS Code **reads
354
+ `.claude/settings.json` by default**. `coderifts hook install --claude`
355
+ therefore arms Copilot agent mode — there is no separate `--copilot` hook
356
+ installer. VS Code Agent hooks **are deny-capable**: PreToolUse honours
357
+ `permissionDecision` allow|deny|ask and **exit 2**.
358
+
359
+ **Property names (CLI 4.8.0):** VS Code passes camelCase (`toolName` /
360
+ `toolInput` / `filePath` / `targetFile`). The hook accepts those alongside
361
+ Claude's snake_case (`tool_name` / `tool_input` / `file_path`). Flattened
362
+ payloads (path on the root object) are accepted. Garbage input is still
363
+ fail-closed.
364
+
365
+ **Matcher limitation (host behaviour, not a CLI bug):** VS Code **parses** hook
366
+ matchers (`Write|Edit|MultiEdit`) but **does not apply them**. The hook fires
367
+ on every tool call. Non-governed tools (Read, Bash, …) and non-contract paths
368
+ exit **0** silently with **no network** and **no API key required** — cheap
369
+ and fail-open for anything that is not a Write/Edit/MultiEdit of the spec. It
370
+ is noisy (every tool call invokes the process) and safe (irrelevant tools are
371
+ not blocked). A spec-path write without a key is still fail-closed.
372
+
373
+ **Honest boundary:** hooks gate the **agent**, not the human. Inline
374
+ completions (ghost text) are not tool calls and are not governed. A developer
375
+ typing in the editor is not a PreToolUse event.
376
+
377
+ **MCP:** `coderifts init --agents` (and `--hosts=copilot`) writes
378
+ `.vscode/mcp.json` with root key **`servers`** — a third shape. Claude Code and
379
+ Cursor use `mcpServers`; Copilot cloud Settings paste also uses `mcpServers`.
380
+ Do not copy one file onto another host.
381
+
350
382
  ### `coderifts status [repo]`
351
383
 
352
384
  **Read-only** cross-layer enforcement report (Runtime / Merge / Deploy, plus
@@ -405,12 +437,14 @@ Provider-native pipeline-protection evidence. This is **not** a host claim:
405
437
 
406
438
  | Layer | VERIFIED when |
407
439
  |-------|----------------|
408
- | `required_check` | Default-branch protection requires `CodeRifts / contract-gate` |
440
+ | `required_check` | Default-branch protection `required_status_checks.checks[]` lists `CodeRifts / contract-gate` **bound to the CodeRifts GitHub App `app_id`** (public metadata: `GET https://api.github.com/apps/coderifts`). Name-only `checks[]` (`app_id` null) is `no_issuer_binding`. The legacy `contexts` array has no issuer field → `legacy_contexts_no_issuer_binding`. Both are **NOT_VERIFIED** (ID621). |
409
441
  | `enforce_admins` | `enforce_admins.enabled` is **true** (admins cannot bypass). `false` = NOT_VERIFIED (admin bypass open) |
410
442
  | `strict_status_checks` | `required_status_checks.strict` is true |
411
- | `environment_protection` | `--env` names a GitHub Environment with protection rules or a deployment branch policy |
412
- | `workflow_contract_gate` | a local `.github/workflows` file references `coderifts/contract-gate@v0` |
413
- | `workflow_deploy_gate` | a local workflow runs `coderifts deploy-gate` |
443
+ | `environment_protection` | `--env` names a GitHub Environment with protection rules **and** an active deploy-gate job sets that same `environment:` (mismatch → `environment_mismatch`) |
444
+ | `workflow_contract_gate` | an **active** job (not `if: false`) has a step whose `uses:` is `coderifts/contract-gate` at a pinned major. Comments, string literals, and commented-out steps are **NOT_VERIFIED** |
445
+ | `workflow_deploy_gate` | an **active** job step **invokes** `coderifts deploy-gate` (not `echo` of the string; not a comment) |
446
+
447
+ **Invariant:** any layer whose evidence cannot be bound to an issuer (our GitHub App id) or to an active job is `NOT_VERIFIED` or `UNVERIFIABLE` — never `VERIFIED`. `inescapable_deploy` remains true only when every layer is `VERIFIED` under that rule.
414
448
 
415
449
  Statuses: `VERIFIED` / `NOT_VERIFIED` / `UNVERIFIABLE(reason)`. Auth:
416
450
  `GITHUB_TOKEN` (never printed). **No token** → API layers `UNVERIFIABLE` with
@@ -419,9 +453,11 @@ CodeRifts GitHub App **cannot** query branch protection (permissions are
419
453
  contents / pull requests / checks — not `administration:read`).
420
454
 
421
455
  `--json` is the quotable evidence document (`spec`:
422
- `provider-enforcement-evidence.v1`): each layer's status, the API endpoint
423
- consulted, `queried_at`. Exit **0** when a report was produced (evidence, not
424
- a gate). GitLab/Bitbucket: no adapter `UNVERIFIABLE`, never stubbed.
456
+ `provider-enforcement-evidence.v2` VERIFIED now requires issuer binding and
457
+ an active YAML-AST job; v1 treated name-only checks and text search as
458
+ VERIFIED): each layer's status, `reason_code`, the API endpoint consulted,
459
+ `queried_at`. Exit **0** when a report was produced (evidence, not a gate).
460
+ GitLab/Bitbucket: no adapter — `UNVERIFIABLE`, never stubbed.
425
461
 
426
462
  ### Agent host files (`coderifts agent-setup`)
427
463
 
package/bin/coderifts.js CHANGED
@@ -92,7 +92,7 @@ program
92
92
  .command('init [template]')
93
93
  .description('Generate a .coderifts.yml from a policy template, or wire governed agent work (--agents)')
94
94
  .option('--agents', 'Wire this repo for governed agent work (MCP config, rule files, host hook, CI gate)')
95
- .option('--hosts <list>', 'With --agents: claude, cursor, or all (default: all)', 'all')
95
+ .option('--hosts <list>', 'With --agents: claude, cursor, copilot, or all (default: all)', 'all')
96
96
  .option('--no-hook', 'With --agents: skip host hook install')
97
97
  .option('--no-workflow', 'With --agents: skip writing .github/workflows/coderifts.yml')
98
98
  .option('--dry-run', 'With --agents: print the plan without writing')