coderifts 3.4.0 → 4.1.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 +64 -0
- package/README.md +29 -11
- package/bin/coderifts.js +3 -2
- package/dist/cli.js +382 -120
- package/package.json +3 -2
- package/scripts/assert-guard-major.js +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 4.1.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`coderifts claude-hook` JSONL GuardEvent log.** When `CODERIFTS_GUARD_EVENT_LOG` is
|
|
8
|
+
set to an absolute file path, the hook appends one JSON object per line:
|
|
9
|
+
`preflight_start` / `preflight_result` / `preflight_unavailable` / `detection_skip`
|
|
10
|
+
plus terminal `hook_blocked` (exit 2) / `hook_continue` (exit 0) /
|
|
11
|
+
`hook_advisory_passthrough` (ADVISORY exit 0). Extra fields: `tool`, `path`,
|
|
12
|
+
`trigger_source:"claude_hook"`, `fingerprint` when the authorize envelope
|
|
13
|
+
returns one, `sessionId` (`CODERIFTS_SESSION_ID` or per-process). Unset env
|
|
14
|
+
→ no file. Log write errors are swallowed and printed to stderr; **exit
|
|
15
|
+
codes are unchanged**. The hook does not claim `enforced` (no
|
|
16
|
+
receipt-verified execution path).
|
|
17
|
+
|
|
18
|
+
## 4.0.0
|
|
19
|
+
|
|
20
|
+
**Breaking** — `coderifts claude-hook` is fail-closed by default. The CLI now
|
|
21
|
+
depends on `@coderifts/agent-guard` ^8.1.1.
|
|
22
|
+
|
|
23
|
+
### Breaking
|
|
24
|
+
- **`claude-hook` default is fail-closed (P0-3/P0-4).** No API key, API
|
|
25
|
+
unreachable, disk unreadable, or edit-apply failure on the spec path →
|
|
26
|
+
**exit 2** (`enforce_indeterminate`), not exit 0. `REQUEST_APPROVAL` →
|
|
27
|
+
exit 2 (`approval_required`). `CONTINUE_WITH_MONITORING` without a
|
|
28
|
+
host-asserted monitoring sink → exit 2. Empty verdict is no longer
|
|
29
|
+
mapped to ALLOW (P0 #5).
|
|
30
|
+
- **Migration:** hosts that ran the hook without a key / without
|
|
31
|
+
`CODERIFTS_STRICT` will now **block** tool calls. Explicit opt-out:
|
|
32
|
+
`CODERIFTS_ADVISORY=1` restores the old soft-allow on "governance could
|
|
33
|
+
not run" sites. `CODERIFTS_STRICT=1` remains fail-closed and cannot be
|
|
34
|
+
weakened by ADVISORY.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
- **`@coderifts/agent-guard` `^6.4.0` → `^8.1.1`.** CLI still calls only
|
|
38
|
+
`deployGate` / `matchGlob` (not the wrap/TOCTOU path). `inescapable_deploy`
|
|
39
|
+
on 8.x is `ENFORCING ∧ ¬bypass ∧ expected_fingerprint` rebound.
|
|
40
|
+
- **`deployReportResiduals`:** rebound-missing is `change_set_not_rebound`,
|
|
41
|
+
never `bypass_open`. Enforcement-axis residuals still named first (same
|
|
42
|
+
order as guard `residuals[]`). CLI AND on the inescapable flag is kept
|
|
43
|
+
(never widen).
|
|
44
|
+
- **`MIN_GUARD_MAJOR`:** 6 → 8 (`scripts/assert-guard-major.js`).
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
- **`coderifts agent-setup` writes `.claude/settings.json`.** PreToolUse hook
|
|
48
|
+
(`matcher: Write|Edit|MultiEdit` → `coderifts claude-hook`, timeout 60)
|
|
49
|
+
beside the six rule files. Fail-closed default (P0-3/P0-4). Explicit
|
|
50
|
+
opt-out documented on stdout: `CODERIFTS_ADVISORY=1`. `--check` is
|
|
51
|
+
byte-stable against the embed (same drift-gate as the six rule files).
|
|
52
|
+
Existing `.claude/settings.json` is skipped (no merge); `--force`
|
|
53
|
+
replaces the **entire** file.
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
- **Installer copy (P0 #9 / e76e846):** `agent-setup` / `init` teach
|
|
57
|
+
guard@8 execution-state **DEFAULT ON**, not the old OFF.
|
|
58
|
+
|
|
59
|
+
## 3.4.0
|
|
60
|
+
|
|
61
|
+
### Agent-setup
|
|
62
|
+
|
|
63
|
+
- Rule files now state that receipts are operation-scoped (a merge receipt never authorizes deploy/publish; re-preflight with the new `context.operation` — do not decline) and that a stale receipt on a changed head needs a new preflight (`verify_receipt` cannot re-diff).
|
|
64
|
+
- Hosts with existing agent-setup files: `coderifts agent-setup --force` to pick up the new paragraphs; `--check` will report drift until then.
|
package/README.md
CHANGED
|
@@ -236,17 +236,22 @@ git config coderifts.specPath api/openapi.yaml # default if omitted
|
|
|
236
236
|
- **User** `~/.claude/settings.json` — personal; applies to all projects on this machine.
|
|
237
237
|
- Matcher is case-sensitive tool names/regex. Exit-2 blocking is **Claude Code** semantics; the same decision discipline at push-time is the git pre-push hook above.
|
|
238
238
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
239
|
+
**Fail-closed default:** missing API key, API unreachable, disk unreadable, or
|
|
240
|
+
edit-apply failure on the spec path is **exit 2** (`enforce_indeterminate`) —
|
|
241
|
+
absence of a key is not ALLOW. `REQUEST_APPROVAL` is **exit 2**
|
|
242
|
+
(`approval_required`). `CONTINUE_WITH_MONITORING` is **exit 2**
|
|
243
|
+
(`monitoring_unwired`) unless the host asserts a sink
|
|
244
|
+
(`CODERIFTS_MONITORING_SINK_WIRED=1` or `git config coderifts.monitoringSinkWired true`)
|
|
245
|
+
— a host claim, not delivery proof.
|
|
246
|
+
|
|
247
|
+
**`CODERIFTS_ADVISORY=1|true`:** explicit opt-out that restores soft-allow on
|
|
248
|
+
“governance could not run” sites. Not implied by a missing key.
|
|
249
|
+
|
|
250
|
+
**`CODERIFTS_STRICT=1|true`:** same fail-closed as the default; cannot be
|
|
251
|
+
weakened by `CODERIFTS_ADVISORY`.
|
|
247
252
|
|
|
248
253
|
```bash
|
|
249
|
-
|
|
254
|
+
CODERIFTS_ADVISORY=1 coderifts claude-hook
|
|
250
255
|
```
|
|
251
256
|
|
|
252
257
|
### `coderifts status [repo]`
|
|
@@ -296,7 +301,8 @@ coderifts enforce --repo owner/repo --apply
|
|
|
296
301
|
|
|
297
302
|
### Agent host files (`coderifts agent-setup`)
|
|
298
303
|
|
|
299
|
-
Writes the six CodeRifts agent-host rule files
|
|
304
|
+
Writes the six CodeRifts agent-host rule files **plus** the Claude Code
|
|
305
|
+
PreToolUse hook settings into the current repo (or `--out <dir>`):
|
|
300
306
|
|
|
301
307
|
| File | Host |
|
|
302
308
|
|------|------|
|
|
@@ -306,6 +312,16 @@ Writes the six CodeRifts agent-host rule files into the current repo (or `--out
|
|
|
306
312
|
| `.github/copilot-instructions.md` | GitHub Copilot |
|
|
307
313
|
| `coderifts-langgraph-policy.js` | LangGraph |
|
|
308
314
|
| `openai-agent-instructions.md` | OpenAI Agents SDK |
|
|
315
|
+
| `.claude/settings.json` | Claude Code PreToolUse → `coderifts claude-hook` (fail-closed) |
|
|
316
|
+
|
|
317
|
+
The generated `.claude/settings.json` matches the recipe above (`matcher`
|
|
318
|
+
`Write|Edit|MultiEdit`, `command` `coderifts claude-hook`, `timeout` 60).
|
|
319
|
+
Default is **fail-closed** (missing key / API down → exit 2). Explicit
|
|
320
|
+
opt-out: `CODERIFTS_ADVISORY=1|true`. `CODERIFTS_STRICT=1` cannot be
|
|
321
|
+
weakened by ADVISORY. Existing files are skipped unless `--force`.
|
|
322
|
+
`--force` **replaces the entire** `.claude/settings.json` (no merge into
|
|
323
|
+
permissions / other hooks). If the file already exists, add the PreToolUse
|
|
324
|
+
recipe by hand or accept a full replace.
|
|
309
325
|
|
|
310
326
|
```bash
|
|
311
327
|
coderifts agent-setup # write (skip existing files)
|
|
@@ -485,7 +501,9 @@ Two honest modes:
|
|
|
485
501
|
|----------|-------------|
|
|
486
502
|
| `CODERIFTS_API_KEY` | API key (alternative to `coderifts login`) |
|
|
487
503
|
| `CODERIFTS_OASDIFF_BIN` | Explicit path to the `oasdiff` binary (overrides auto-detect) |
|
|
488
|
-
| `CODERIFTS_STRICT` | `claude-hook
|
|
504
|
+
| `CODERIFTS_STRICT` | `claude-hook`: `1` or `true` pins fail-closed (cannot be weakened by `CODERIFTS_ADVISORY`). Default is already fail-closed. |
|
|
505
|
+
| `CODERIFTS_ADVISORY` | `claude-hook`: `1` or `true` is the **explicit** soft-allow opt-out on “governance could not run” sites. Absence of a key is not this flag. |
|
|
506
|
+
| `CODERIFTS_MONITORING_SINK_WIRED` | `claude-hook`: `1` or `true` host-asserts a monitoring sink so `CONTINUE_WITH_MONITORING` may exit 0. Not delivery proof. |
|
|
489
507
|
| `CODERIFTS_DEPLOY_ENFORCE` | `deploy-gate`: `true` marks the step enforcing (same as `--enforce`); `unknown` is explicitly unobservable (never assume ENFORCING) |
|
|
490
508
|
| `NO_COLOR` | Disable colored output |
|
|
491
509
|
|
package/bin/coderifts.js
CHANGED
|
@@ -259,8 +259,9 @@ program
|
|
|
259
259
|
program
|
|
260
260
|
.command('claude-hook')
|
|
261
261
|
.description(
|
|
262
|
-
'Claude Code PreToolUse gate: block contract-touching Write|Edit|MultiEdit on BLOCK/STOP
|
|
263
|
-
+ '(exit 2).
|
|
262
|
+
'Claude Code PreToolUse gate: block contract-touching Write|Edit|MultiEdit on BLOCK/STOP/'
|
|
263
|
+
+ 'REQUEST_APPROVAL (exit 2). Fail-closed when governance cannot run; '
|
|
264
|
+
+ 'CODERIFTS_ADVISORY=1 is the explicit soft opt-out.',
|
|
264
265
|
)
|
|
265
266
|
.addHelpText('after', () => {
|
|
266
267
|
const { USAGE } = require('../src/commands/claude-hook');
|
package/dist/cli.js
CHANGED
|
@@ -3007,7 +3007,7 @@ var require_package = __commonJS({
|
|
|
3007
3007
|
"package.json"(exports2, module2) {
|
|
3008
3008
|
module2.exports = {
|
|
3009
3009
|
name: "coderifts",
|
|
3010
|
-
version: "
|
|
3010
|
+
version: "4.1.0",
|
|
3011
3011
|
description: "Detect breaking API changes from the command line. Works locally or with the CodeRifts cloud API.",
|
|
3012
3012
|
author: "CodeRifts <hello@coderifts.com>",
|
|
3013
3013
|
license: "MIT",
|
|
@@ -3020,6 +3020,7 @@ var require_package = __commonJS({
|
|
|
3020
3020
|
"bin/",
|
|
3021
3021
|
"scripts/",
|
|
3022
3022
|
"README.md",
|
|
3023
|
+
"CHANGELOG.md",
|
|
3023
3024
|
"corpus/"
|
|
3024
3025
|
],
|
|
3025
3026
|
keywords: [
|
|
@@ -3051,7 +3052,7 @@ var require_package = __commonJS({
|
|
|
3051
3052
|
test: "node --test test/*.test.js"
|
|
3052
3053
|
},
|
|
3053
3054
|
dependencies: {
|
|
3054
|
-
"@coderifts/agent-guard": "^
|
|
3055
|
+
"@coderifts/agent-guard": "^8.1.1",
|
|
3055
3056
|
chalk: "^4.1.2",
|
|
3056
3057
|
"cli-table3": "^0.6.4",
|
|
3057
3058
|
commander: "^12.0.0",
|
|
@@ -67465,43 +67466,53 @@ var require_guard = __commonJS({
|
|
|
67465
67466
|
}
|
|
67466
67467
|
const enforceable = receiptVerified && (kind === "ALLOW" || sinkWired);
|
|
67467
67468
|
if (enforceable) {
|
|
67468
|
-
const execStateMode = config.requireExecutionStateMatch;
|
|
67469
|
-
if (execStateMode ===
|
|
67470
|
-
|
|
67471
|
-
|
|
67472
|
-
|
|
67473
|
-
|
|
67474
|
-
|
|
67475
|
-
environment: config.environment
|
|
67476
|
-
},
|
|
67477
|
-
envelope
|
|
67469
|
+
const execStateMode = config.requireExecutionStateMatch === void 0 ? true : config.requireExecutionStateMatch;
|
|
67470
|
+
if (execStateMode === false) {
|
|
67471
|
+
emit(config, {
|
|
67472
|
+
type: "execution_state_check_disabled",
|
|
67473
|
+
at: iso(),
|
|
67474
|
+
decisionId: envelope.decision_id,
|
|
67475
|
+
cause: "requireExecutionStateMatch_false"
|
|
67478
67476
|
});
|
|
67479
|
-
|
|
67480
|
-
|
|
67481
|
-
|
|
67482
|
-
|
|
67483
|
-
|
|
67484
|
-
|
|
67485
|
-
|
|
67486
|
-
|
|
67487
|
-
|
|
67488
|
-
|
|
67489
|
-
|
|
67490
|
-
|
|
67491
|
-
|
|
67492
|
-
|
|
67493
|
-
|
|
67494
|
-
|
|
67495
|
-
|
|
67496
|
-
|
|
67497
|
-
|
|
67498
|
-
|
|
67499
|
-
|
|
67500
|
-
|
|
67501
|
-
|
|
67502
|
-
|
|
67503
|
-
|
|
67477
|
+
const offVerdict = kind === "ALLOW" ? { kind: "ALLOW", action: "CONTINUE", envelope, receiptVerified } : { kind: "MONITOR", action: "CONTINUE_WITH_MONITORING", envelope, receiptVerified };
|
|
67478
|
+
return runUnenforced(config, executeFactory, envelope, offVerdict, true, redacted, freshBasis, cwBasis);
|
|
67479
|
+
}
|
|
67480
|
+
const et = (0, execution_time_fingerprint_js_1.checkExecutionTimeFingerprint)({
|
|
67481
|
+
artifacts: detection.artifacts,
|
|
67482
|
+
context: {
|
|
67483
|
+
operation: config.operation ?? "tool_call",
|
|
67484
|
+
environment: config.environment
|
|
67485
|
+
},
|
|
67486
|
+
envelope
|
|
67487
|
+
});
|
|
67488
|
+
if (!et.match) {
|
|
67489
|
+
const unmeasurable = (0, execution_time_fingerprint_js_1.isUnmeasurableExecutionStateReason)(et.reason);
|
|
67490
|
+
if (execStateMode === true) {
|
|
67491
|
+
breakerRecord(config);
|
|
67492
|
+
return closedIntegrity(config, unmeasurable ? "EXECUTION_STATE_UNMEASURABLE" : "EXECUTION_STATE_DRIFT", failPolicy, fctx, cwctx, redacted, detection.artifacts);
|
|
67493
|
+
}
|
|
67494
|
+
if (unmeasurable) {
|
|
67495
|
+
emit(config, {
|
|
67496
|
+
type: "execution_state_unmeasurable",
|
|
67497
|
+
at: iso(),
|
|
67498
|
+
decisionId: envelope.decision_id,
|
|
67499
|
+
current_fingerprint: et.current_fingerprint,
|
|
67500
|
+
authorized_fingerprint: et.authorized_fingerprint,
|
|
67501
|
+
reason: et.reason,
|
|
67502
|
+
note: execution_time_fingerprint_js_1.EXECUTION_STATE_UNMEASURABLE_NOTE
|
|
67503
|
+
});
|
|
67504
|
+
} else {
|
|
67505
|
+
emit(config, {
|
|
67506
|
+
type: "execution_state_drift_observed",
|
|
67507
|
+
at: iso(),
|
|
67508
|
+
decisionId: envelope.decision_id,
|
|
67509
|
+
current_fingerprint: et.current_fingerprint,
|
|
67510
|
+
authorized_fingerprint: et.authorized_fingerprint,
|
|
67511
|
+
reason: et.reason
|
|
67512
|
+
});
|
|
67504
67513
|
}
|
|
67514
|
+
const warnVerdict = kind === "ALLOW" ? { kind: "ALLOW", action: "CONTINUE", envelope, receiptVerified } : { kind: "MONITOR", action: "CONTINUE_WITH_MONITORING", envelope, receiptVerified };
|
|
67515
|
+
return runUnenforced(config, executeFactory, envelope, warnVerdict, true, redacted, freshBasis, cwBasis);
|
|
67505
67516
|
}
|
|
67506
67517
|
const approved = kind === "ALLOW" ? { kind: "ALLOW", action: "CONTINUE", envelope, receiptVerified: true } : { kind: "MONITOR", action: "CONTINUE_WITH_MONITORING", envelope, receiptVerified: true };
|
|
67507
67518
|
return runEnforced(config, executeFactory, approved, redacted, freshBasis, cwBasis);
|
|
@@ -69861,24 +69872,25 @@ var require_merge_gate = __commonJS({
|
|
|
69861
69872
|
if (!isAllowClass(receipt, allowWarnMerge)) {
|
|
69862
69873
|
return fail("failure", "decision_not_allow");
|
|
69863
69874
|
}
|
|
69864
|
-
const
|
|
69875
|
+
const protectionOk = enforcement_state === "ENFORCING" && protection.admin_bypass_possible === false;
|
|
69876
|
+
const appBound = protection.required_check_app_bound === true;
|
|
69877
|
+
const rebound = rc.expected_fingerprint != null && String(rc.expected_fingerprint).length > 0;
|
|
69878
|
+
const inescapable_merge = protectionOk && appBound && rebound;
|
|
69865
69879
|
const residuals = [];
|
|
69866
|
-
if (!
|
|
69880
|
+
if (!protectionOk) {
|
|
69867
69881
|
if (enforcement_state === "ENFORCING")
|
|
69868
69882
|
residuals.push("admin_bypass_open");
|
|
69869
69883
|
else if (enforcement_state === "ADVISORY")
|
|
69870
69884
|
residuals.push("protection_advisory_only");
|
|
69871
69885
|
else
|
|
69872
69886
|
residuals.push("protection_not_configured");
|
|
69887
|
+
} else if (protection.required_check_app_bound === true) {
|
|
69888
|
+
} else if (protection.required_check_app_bound === false) {
|
|
69889
|
+
residuals.push("required_check_app_not_bound");
|
|
69873
69890
|
} else {
|
|
69874
|
-
|
|
69875
|
-
} else if (protection.required_check_app_bound === false) {
|
|
69876
|
-
residuals.push("required_check_app_not_bound");
|
|
69877
|
-
} else {
|
|
69878
|
-
residuals.push("required_check_app_binding_unknown");
|
|
69879
|
-
}
|
|
69891
|
+
residuals.push("required_check_app_binding_unknown");
|
|
69880
69892
|
}
|
|
69881
|
-
if (
|
|
69893
|
+
if (!rebound) {
|
|
69882
69894
|
residuals.push("change_set_not_rebound");
|
|
69883
69895
|
}
|
|
69884
69896
|
const residual = residuals.length > 0 ? residuals[0] : void 0;
|
|
@@ -70001,15 +70013,17 @@ var require_deploy_gate = __commonJS({
|
|
|
70001
70013
|
if (!isAllowClass(receipt, allowWarnDeploy)) {
|
|
70002
70014
|
return deny("failure", "decision_not_allow");
|
|
70003
70015
|
}
|
|
70004
|
-
const
|
|
70016
|
+
const enforcementOk = enforcement_state === "ENFORCING" && enf.bypass_possible === false;
|
|
70017
|
+
const rebound = rc.expected_fingerprint != null && String(rc.expected_fingerprint).length > 0;
|
|
70018
|
+
const inescapable_deploy = enforcementOk && rebound;
|
|
70005
70019
|
const residuals = [];
|
|
70006
|
-
if (!
|
|
70020
|
+
if (!enforcementOk) {
|
|
70007
70021
|
if (enforcement_state === "ENFORCING")
|
|
70008
70022
|
residuals.push("bypass_open");
|
|
70009
70023
|
else
|
|
70010
70024
|
residuals.push("enforcement_not_configured");
|
|
70011
70025
|
}
|
|
70012
|
-
if (
|
|
70026
|
+
if (!rebound) {
|
|
70013
70027
|
residuals.push("change_set_not_rebound");
|
|
70014
70028
|
}
|
|
70015
70029
|
const residual = residuals.length > 0 ? residuals[0] : void 0;
|
|
@@ -70298,6 +70312,7 @@ var require_with_coderifts = __commonJS({
|
|
|
70298
70312
|
var COMPOSITION_CALL_POLICY_COMPLETE = false;
|
|
70299
70313
|
var RESIDUAL_CALL_POLICY_INCOMPLETE = "composition_call_policy_incomplete";
|
|
70300
70314
|
var RESIDUAL_FRESHNESS_NOT_CONFIGURED = "composition_freshness_not_configured";
|
|
70315
|
+
var RESIDUAL_CALLS_OUTSIDE_GUARDED_PATH = "calls_outside_guarded_path_invisible";
|
|
70301
70316
|
var RESIDUAL_FORCED_READONLY = "composition_forced_readonly_on_heuristic_mutator";
|
|
70302
70317
|
var RESIDUAL_UNKNOWN_READONLY = "composition_unknown_treated_as_readonly";
|
|
70303
70318
|
var COVERAGE_STRENGTH = {
|
|
@@ -70309,6 +70324,30 @@ var require_with_coderifts = __commonJS({
|
|
|
70309
70324
|
function coverageRank(coverage) {
|
|
70310
70325
|
return Object.prototype.hasOwnProperty.call(COVERAGE_STRENGTH, coverage) ? COVERAGE_STRENGTH[coverage] : void 0;
|
|
70311
70326
|
}
|
|
70327
|
+
function isEnforcingStrict(input) {
|
|
70328
|
+
return input.profile === "ENFORCING_STRICT";
|
|
70329
|
+
}
|
|
70330
|
+
function enforcingStrictWeakenFlags(input) {
|
|
70331
|
+
const flags = [];
|
|
70332
|
+
if (input.requireCoverage !== void 0) {
|
|
70333
|
+
const rank = coverageRank(input.requireCoverage);
|
|
70334
|
+
if (rank !== void 0 && rank < COVERAGE_STRENGTH.COMPLETE)
|
|
70335
|
+
flags.push("requireCoverage");
|
|
70336
|
+
}
|
|
70337
|
+
if (input.requireFreshness === false)
|
|
70338
|
+
flags.push("requireFreshness");
|
|
70339
|
+
if (input.requireExecutionStateMatch === false || input.requireExecutionStateMatch === "warn") {
|
|
70340
|
+
flags.push("requireExecutionStateMatch");
|
|
70341
|
+
}
|
|
70342
|
+
if (input.requireConditionalWrite === false)
|
|
70343
|
+
flags.push("requireConditionalWrite");
|
|
70344
|
+
const reg = input.registry ?? {};
|
|
70345
|
+
if (reg.failOnUnguardedMutator === false)
|
|
70346
|
+
flags.push("failOnUnguardedMutator");
|
|
70347
|
+
if (reg.unknownToolPolicy === "readonly")
|
|
70348
|
+
flags.push("unknownToolPolicy");
|
|
70349
|
+
return flags;
|
|
70350
|
+
}
|
|
70312
70351
|
async function safeOnSettledCall(onSettledCall, payload) {
|
|
70313
70352
|
try {
|
|
70314
70353
|
await Promise.resolve(onSettledCall(payload));
|
|
@@ -70495,6 +70534,18 @@ var require_with_coderifts = __commonJS({
|
|
|
70495
70534
|
if (input.requireCoverage !== void 0 && coverageRank(input.requireCoverage) === void 0) {
|
|
70496
70535
|
problems.push(`\`requireCoverage\` must be one of COMPLETE | PARTIAL | BYPASSED | UNKNOWN (got ${JSON.stringify(input.requireCoverage)})`);
|
|
70497
70536
|
}
|
|
70537
|
+
if (input.profile !== void 0 && input.profile !== "ENFORCING_STRICT") {
|
|
70538
|
+
problems.push(`\`profile\` must be 'ENFORCING_STRICT' when set (got ${JSON.stringify(input.profile)})`);
|
|
70539
|
+
}
|
|
70540
|
+
if (isEnforcingStrict(input)) {
|
|
70541
|
+
const weaken = enforcingStrictWeakenFlags(input);
|
|
70542
|
+
if (weaken.length > 0) {
|
|
70543
|
+
problems.push(`ENFORCING_STRICT cannot be weakened: ${weaken.join(", ")} conflicts`);
|
|
70544
|
+
}
|
|
70545
|
+
if (typeof input.resolvePriorContent !== "function") {
|
|
70546
|
+
problems.push("ENFORCING_STRICT cannot be weakened: resolvePriorContent conflicts");
|
|
70547
|
+
}
|
|
70548
|
+
}
|
|
70498
70549
|
if (problems.length > 0) {
|
|
70499
70550
|
throw new Error(`withCodeRifts: construction aborted \u2014 ${problems.length} condition(s):
|
|
70500
70551
|
` + problems.map((p) => ` - ${p}`).join("\n"));
|
|
@@ -70533,20 +70584,27 @@ var require_with_coderifts = __commonJS({
|
|
|
70533
70584
|
if (input.requireExecutionStateMatch !== void 0) {
|
|
70534
70585
|
guard.requireExecutionStateMatch = input.requireExecutionStateMatch;
|
|
70535
70586
|
}
|
|
70587
|
+
const strict = isEnforcingStrict(input);
|
|
70588
|
+
if (strict) {
|
|
70589
|
+
guard.requireFreshness = true;
|
|
70590
|
+
guard.requireConditionalWrite = true;
|
|
70591
|
+
guard.requireExecutionStateMatch = true;
|
|
70592
|
+
}
|
|
70536
70593
|
const config = {
|
|
70537
70594
|
guard,
|
|
70538
|
-
unknownToolPolicy: reg.unknownToolPolicy ?? "mutating",
|
|
70595
|
+
unknownToolPolicy: strict ? reg.unknownToolPolicy === "reject" ? "reject" : "mutating" : reg.unknownToolPolicy ?? "mutating",
|
|
70539
70596
|
classify: reg.classify,
|
|
70540
70597
|
binders: reg.binders,
|
|
70541
70598
|
forceReadonly: reg.forceReadonly,
|
|
70542
|
-
failOnUnguardedMutator: reg.failOnUnguardedMutator
|
|
70599
|
+
failOnUnguardedMutator: strict ? true : reg.failOnUnguardedMutator
|
|
70543
70600
|
};
|
|
70601
|
+
const requireCoverage = strict ? "COMPLETE" : input.requireCoverage;
|
|
70544
70602
|
const { tools, report } = (0, tool_registry_js_1.guardToolRegistry)(input.tools, config);
|
|
70545
|
-
if (
|
|
70546
|
-
const requiredRank = coverageRank(
|
|
70603
|
+
if (requireCoverage !== void 0) {
|
|
70604
|
+
const requiredRank = coverageRank(requireCoverage);
|
|
70547
70605
|
const actualRank = coverageRank(report.coverage) ?? -1;
|
|
70548
70606
|
if (requiredRank !== void 0 && actualRank < requiredRank) {
|
|
70549
|
-
throw new Error(`withCodeRifts: requireCoverage not met \u2014 registry coverage '${report.coverage}' is weaker than required '${
|
|
70607
|
+
throw new Error(`withCodeRifts: requireCoverage not met \u2014 registry coverage '${report.coverage}' is weaker than required '${requireCoverage}' (strength ordering COMPLETE > PARTIAL > BYPASSED > UNKNOWN). requireCoverage constrains the REGISTRY tool-boundary surface ONLY; a green construction here is NOT a product-level runtime-inescapability guarantee \u2014 composition_assurance.inescapable_runtime stays false until receipt carry-forward and a freshness-safe prior for write-style calls land.`);
|
|
70550
70608
|
}
|
|
70551
70609
|
}
|
|
70552
70610
|
const compositionInescapableRuntime = report.claim.inescapable_runtime && COMPOSITION_CALL_POLICY_COMPLETE;
|
|
@@ -70561,9 +70619,12 @@ var require_with_coderifts = __commonJS({
|
|
|
70561
70619
|
if (!freshness_resolver_wired) {
|
|
70562
70620
|
residuals.push(RESIDUAL_FRESHNESS_NOT_CONFIGURED);
|
|
70563
70621
|
}
|
|
70564
|
-
if (input.requireConditionalWrite === true) {
|
|
70622
|
+
if (input.requireConditionalWrite === true || strict) {
|
|
70565
70623
|
residuals.push("composition_unconditional_write_under_policy");
|
|
70566
70624
|
}
|
|
70625
|
+
if (strict) {
|
|
70626
|
+
residuals.push(RESIDUAL_CALLS_OUTSIDE_GUARDED_PATH);
|
|
70627
|
+
}
|
|
70567
70628
|
const composition_assurance = {
|
|
70568
70629
|
coverage: "PARTIAL",
|
|
70569
70630
|
inescapable_runtime: compositionInescapableRuntime,
|
|
@@ -71755,7 +71816,8 @@ var require_deploy_gate2 = __commonJS({
|
|
|
71755
71816
|
if (enforcement === "ENFORCING") out.push("bypass_open");
|
|
71756
71817
|
else if (enforcement === "ADVISORY") out.push("deploy_gate_advisory");
|
|
71757
71818
|
else if (enforcement === "ABSENT") out.push("deploy_path_ungated");
|
|
71758
|
-
}
|
|
71819
|
+
}
|
|
71820
|
+
if (change_set_rebound !== true) {
|
|
71759
71821
|
out.push("change_set_not_rebound");
|
|
71760
71822
|
}
|
|
71761
71823
|
return out;
|
|
@@ -71793,8 +71855,8 @@ var require_deploy_gate2 = __commonJS({
|
|
|
71793
71855
|
if (expected_body_hash != null) requiredContext.expected_body_hash = expected_body_hash;
|
|
71794
71856
|
}
|
|
71795
71857
|
const gate = deployGate({ deployTarget: { environment, artifact_id }, receipt, requiredContext });
|
|
71796
|
-
const enforcement_inescapable =
|
|
71797
|
-
const inescapable_deploy =
|
|
71858
|
+
const enforcement_inescapable = attested_enforcement === "ENFORCING" && !!(observed_cd_enforcement && observed_cd_enforcement.bypass_possible === false);
|
|
71859
|
+
const inescapable_deploy = gate.inescapable_deploy === true && change_set_rebound === true;
|
|
71798
71860
|
return {
|
|
71799
71861
|
deploy_check_status: gate.state,
|
|
71800
71862
|
reason: gate.reason,
|
|
@@ -73285,9 +73347,9 @@ generator_detection: true
|
|
|
73285
73347
|
chalk.dim(" (uses YOUR gh credentials; CodeRifts never takes admin rights)."),
|
|
73286
73348
|
chalk.dim(" To attest cross-layer ENFORCING after wiring: coderifts enforce --apply"),
|
|
73287
73349
|
chalk.dim(" (dry-run without --apply; does not auto-flip defaults)."),
|
|
73288
|
-
chalk.dim(" For agent tool-call guarding
|
|
73289
|
-
chalk.dim("
|
|
73290
|
-
chalk.dim(" (
|
|
73350
|
+
chalk.dim(" For agent tool-call guarding: @coderifts/agent-guard requireExecutionStateMatch"),
|
|
73351
|
+
chalk.dim(" defaults ON (absent\u2192true) \u2014 observed drift STOPS. Opt-down: 'warn' (emit, proceed)"),
|
|
73352
|
+
chalk.dim(" or false (proceed-on-drift) \u2014 see: coderifts agent-setup"),
|
|
73291
73353
|
chalk.dim(" Agent-using repo? Run: coderifts agent-setup")
|
|
73292
73354
|
]);
|
|
73293
73355
|
module2.exports = {
|
|
@@ -99997,7 +100059,8 @@ var require_agent_host_files_embedded = __commonJS({
|
|
|
99997
100059
|
".cursor/rules/coderifts.mdc",
|
|
99998
100060
|
".github/copilot-instructions.md",
|
|
99999
100061
|
"coderifts-langgraph-policy.js",
|
|
100000
|
-
"openai-agent-instructions.md"
|
|
100062
|
+
"openai-agent-instructions.md",
|
|
100063
|
+
".claude/settings.json"
|
|
100001
100064
|
]);
|
|
100002
100065
|
var AGENT_HOST_FILES = Object.freeze({
|
|
100003
100066
|
"AGENTS.md": '<!-- GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js -->\n\n# CodeRifts agent rules\n\nCall `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\n\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\n\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\n\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\n\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not "proceed with caution" without monitoring.\n\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\n\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\n\nA receipt is operation-scoped: a merge receipt never authorizes a deploy or publish. When the operation changes, you MUST call `preflight_change_set` again with the new `context.operation` \u2014 do not decline, re-preflight.\n\nA stale or superseded receipt on a changed head requires a NEW preflight \u2014 `verify_receipt` cannot re-diff.\n\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\n\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.\n',
|
|
@@ -100005,7 +100068,8 @@ var require_agent_host_files_embedded = __commonJS({
|
|
|
100005
100068
|
".cursor/rules/coderifts.mdc": '---\ndescription: CodeRifts API governance \u2014 when to preflight and how to branch\nglobs:\nalwaysApply: true\n---\n\n<!-- GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js -->\n\n# CodeRifts\n\nCall `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\n\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\n\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\n\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\n\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not "proceed with caution" without monitoring.\n\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\n\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\n\nA receipt is operation-scoped: a merge receipt never authorizes a deploy or publish. When the operation changes, you MUST call `preflight_change_set` again with the new `context.operation` \u2014 do not decline, re-preflight.\n\nA stale or superseded receipt on a changed head requires a NEW preflight \u2014 `verify_receipt` cannot re-diff.\n\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\n\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.\n',
|
|
100006
100069
|
".github/copilot-instructions.md": '<!-- GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js -->\n\n# CodeRifts instructions for GitHub Copilot\n\nCall `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\n\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\n\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\n\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\n\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not "proceed with caution" without monitoring.\n\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\n\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\n\nA receipt is operation-scoped: a merge receipt never authorizes a deploy or publish. When the operation changes, you MUST call `preflight_change_set` again with the new `context.operation` \u2014 do not decline, re-preflight.\n\nA stale or superseded receipt on a changed head requires a NEW preflight \u2014 `verify_receipt` cannot re-diff.\n\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\n\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.\n',
|
|
100007
100070
|
"coderifts-langgraph-policy.js": '// GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js\n// System policy string for a LangGraph (or similar) agent. Content is generated;\n// identical rule sentences to AGENTS.md / other formats.\n\'use strict\';\n\nmodule.exports = "Call `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\\n\\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\\n\\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\\n\\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\\n\\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not \\"proceed with caution\\" without monitoring.\\n\\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\\n\\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\\n\\nA receipt is operation-scoped: a merge receipt never authorizes a deploy or publish. When the operation changes, you MUST call `preflight_change_set` again with the new `context.operation` \u2014 do not decline, re-preflight.\\n\\nA stale or superseded receipt on a changed head requires a NEW preflight \u2014 `verify_receipt` cannot re-diff.\\n\\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\\n\\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.";\n',
|
|
100008
|
-
"openai-agent-instructions.md": '<!-- GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js -->\n\n# CodeRifts agent instructions\n\nCall `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\n\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\n\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\n\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\n\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not "proceed with caution" without monitoring.\n\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\n\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\n\nA receipt is operation-scoped: a merge receipt never authorizes a deploy or publish. When the operation changes, you MUST call `preflight_change_set` again with the new `context.operation` \u2014 do not decline, re-preflight.\n\nA stale or superseded receipt on a changed head requires a NEW preflight \u2014 `verify_receipt` cannot re-diff.\n\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\n\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.\n'
|
|
100071
|
+
"openai-agent-instructions.md": '<!-- GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js -->\n\n# CodeRifts agent instructions\n\nCall `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\n\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\n\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\n\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\n\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not "proceed with caution" without monitoring.\n\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\n\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\n\nA receipt is operation-scoped: a merge receipt never authorizes a deploy or publish. When the operation changes, you MUST call `preflight_change_set` again with the new `context.operation` \u2014 do not decline, re-preflight.\n\nA stale or superseded receipt on a changed head requires a NEW preflight \u2014 `verify_receipt` cannot re-diff.\n\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\n\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.\n',
|
|
100072
|
+
".claude/settings.json": '{\n "hooks": {\n "PreToolUse": [\n {\n "matcher": "Write|Edit|MultiEdit",\n "hooks": [\n {\n "type": "command",\n "command": "coderifts claude-hook",\n "timeout": 60\n }\n ]\n }\n ]\n }\n}\n'
|
|
100009
100073
|
});
|
|
100010
100074
|
module2.exports = { AGENT_HOST_FILES, AGENT_HOST_PATHS };
|
|
100011
100075
|
}
|
|
@@ -100145,27 +100209,52 @@ ${USAGE}` };
|
|
|
100145
100209
|
for (const line of AGENT_SETUP_WARN_TELEMETRY_LINES) {
|
|
100146
100210
|
log(line);
|
|
100147
100211
|
}
|
|
100212
|
+
if (summary.skipped.includes(".claude/settings.json")) {
|
|
100213
|
+
for (const line of AGENT_SETUP_HOOK_SKIP_LINES) {
|
|
100214
|
+
log(line);
|
|
100215
|
+
}
|
|
100216
|
+
} else if (summary.written.includes(".claude/settings.json")) {
|
|
100217
|
+
for (const line of AGENT_SETUP_HOOK_WIRE_LINES) {
|
|
100218
|
+
log(line);
|
|
100219
|
+
}
|
|
100220
|
+
}
|
|
100148
100221
|
}
|
|
100149
100222
|
if (doExit) process.exit(0);
|
|
100150
100223
|
return { exitCode: 0, code: "OK", outDir, ...summary };
|
|
100151
100224
|
}
|
|
100152
100225
|
var AGENT_SETUP_WARN_TELEMETRY_LINES = Object.freeze([
|
|
100153
|
-
chalk.bold("
|
|
100154
|
-
chalk.dim("
|
|
100155
|
-
chalk.dim(" (
|
|
100156
|
-
chalk.dim("
|
|
100226
|
+
chalk.bold(" Execution-state recheck (guard@8): DEFAULT ON"),
|
|
100227
|
+
chalk.dim(" requireExecutionStateMatch absent/true \u2192 enforce: mismatch \u2192 EXECUTION_STATE_DRIFT"),
|
|
100228
|
+
chalk.dim(" (factory does not run); missing fingerprint/artifacts \u2192 EXECUTION_STATE_UNMEASURABLE (STOP)."),
|
|
100229
|
+
chalk.dim(" Opt-down: requireExecutionStateMatch: 'warn' (emit, then run unenforced on mismatch)"),
|
|
100230
|
+
chalk.dim(" or false (no recheck \u2014 v7 proceed-on-drift). Must be set explicitly."),
|
|
100231
|
+
chalk.dim(" Warn emits (then PROCEEDS \u2014 never blocks in warn mode):"),
|
|
100157
100232
|
chalk.dim(" \xB7 real drift \u2192 execution_state_drift_observed (loud)"),
|
|
100158
100233
|
chalk.dim(" \xB7 missing artifacts/fingerprint \u2192 execution_state_unmeasurable (quiet)"),
|
|
100159
|
-
chalk.dim(" Enforce later with requireExecutionStateMatch: true only when ready."),
|
|
100160
100234
|
chalk.dim(" Merge gate block is separate: coderifts setup-required-check --apply")
|
|
100161
100235
|
]);
|
|
100236
|
+
var AGENT_SETUP_HOOK_WIRE_LINES = Object.freeze([
|
|
100237
|
+
chalk.bold(" Claude Code hook (.claude/settings.json): fail-closed PreToolUse"),
|
|
100238
|
+
chalk.dim(" matcher Write|Edit|MultiEdit \u2192 coderifts claude-hook (exit 2 = BLOCK)."),
|
|
100239
|
+
chalk.dim(" Missing key / API down / unreadable spec \u2192 exit 2 (enforce_indeterminate), not ALLOW."),
|
|
100240
|
+
chalk.dim(" Opt-out: CODERIFTS_ADVISORY=1|true restores soft-allow when governance could not run."),
|
|
100241
|
+
chalk.dim(" CODERIFTS_STRICT=1 cannot be weakened by ADVISORY.")
|
|
100242
|
+
]);
|
|
100243
|
+
var AGENT_SETUP_HOOK_SKIP_LINES = Object.freeze([
|
|
100244
|
+
chalk.yellow(" .claude/settings.json exists \u2014 left unchanged (installer does not merge)."),
|
|
100245
|
+
chalk.dim(" --force replaces the ENTIRE file (not just the PreToolUse hook)."),
|
|
100246
|
+
chalk.dim(" To wire fail-closed: add PreToolUse \u2192 coderifts claude-hook by hand, or --force."),
|
|
100247
|
+
chalk.dim(" Opt-out if wired: CODERIFTS_ADVISORY=1|true. CODERIFTS_STRICT cannot weaken.")
|
|
100248
|
+
]);
|
|
100162
100249
|
module2.exports = {
|
|
100163
100250
|
runAgentSetup,
|
|
100164
100251
|
parseAgentSetupArgs,
|
|
100165
100252
|
AGENT_HOST_FILES,
|
|
100166
100253
|
AGENT_HOST_PATHS,
|
|
100167
100254
|
USAGE,
|
|
100168
|
-
AGENT_SETUP_WARN_TELEMETRY_LINES
|
|
100255
|
+
AGENT_SETUP_WARN_TELEMETRY_LINES,
|
|
100256
|
+
AGENT_SETUP_HOOK_WIRE_LINES,
|
|
100257
|
+
AGENT_SETUP_HOOK_SKIP_LINES
|
|
100169
100258
|
};
|
|
100170
100259
|
}
|
|
100171
100260
|
});
|
|
@@ -101133,21 +101222,23 @@ var require_claude_hook = __commonJS({
|
|
|
101133
101222
|
"configured contract spec path. Reads JSON context from STDIN.",
|
|
101134
101223
|
"",
|
|
101135
101224
|
"Exit map (Claude Code semantics \u2014 get this exactly right):",
|
|
101136
|
-
" 2 BLOCK \u2014 tool call cancelled (
|
|
101137
|
-
"
|
|
101225
|
+
" 2 BLOCK \u2014 tool call cancelled (BLOCK/STOP, REQUEST_APPROVAL, unrecognised action,",
|
|
101226
|
+
" governance could not run, CONTINUE_WITH_MONITORING without a wired sink)",
|
|
101227
|
+
" 0 allow \u2014 CONTINUE, or CWM with a host-asserted monitoring sink, or non-contract skip",
|
|
101138
101228
|
" 1 NEVER used for security deny (Claude treats exit 1 as non-blocking; action proceeds)",
|
|
101139
101229
|
"",
|
|
101140
|
-
"
|
|
101141
|
-
" no API key
|
|
101142
|
-
"
|
|
101143
|
-
"
|
|
101144
|
-
"
|
|
101145
|
-
"
|
|
101230
|
+
"Fail-closed DEFAULT (exit 2, reason enforce_indeterminate) when governance could not run:",
|
|
101231
|
+
" no API key / API unreachable / disk unreadable / edit-apply failure on the spec path",
|
|
101232
|
+
" Absence of a key is not permission. Explicit opt-out: CODERIFTS_ADVISORY=1|true.",
|
|
101233
|
+
"",
|
|
101234
|
+
"Still exit 0 (nothing to govern): unparseable stdin, missing file_path, non-spec path,",
|
|
101235
|
+
"identical content.",
|
|
101146
101236
|
"",
|
|
101147
|
-
"
|
|
101148
|
-
"
|
|
101149
|
-
"
|
|
101150
|
-
"
|
|
101237
|
+
"CONTINUE_WITH_MONITORING: allow only if the host asserts a sink",
|
|
101238
|
+
" (CODERIFTS_MONITORING_SINK_WIRED=1|true or git config coderifts.monitoringSinkWired).",
|
|
101239
|
+
" Host claim \u2014 not delivery proof (same class as GuardConfig.monitoringSinkWired).",
|
|
101240
|
+
"",
|
|
101241
|
+
"CODERIFTS_STRICT=1|true: same fail-closed as default; cannot be weakened by ADVISORY.",
|
|
101151
101242
|
"",
|
|
101152
101243
|
"Spec path (same source as git pre-push hook):",
|
|
101153
101244
|
" git config coderifts.specPath (default: api/openapi.yaml)",
|
|
@@ -101158,21 +101249,83 @@ var require_claude_hook = __commonJS({
|
|
|
101158
101249
|
"",
|
|
101159
101250
|
"Push-time equivalent: coderifts hook install (git exit 1 on BLOCK)."
|
|
101160
101251
|
].join("\n");
|
|
101161
|
-
function
|
|
101162
|
-
const v = env && env
|
|
101252
|
+
function isEnvFlag(env, name) {
|
|
101253
|
+
const v = env && env[name];
|
|
101163
101254
|
if (v == null || v === "") return false;
|
|
101164
101255
|
const s = String(v).trim().toLowerCase();
|
|
101165
101256
|
return s === "1" || s === "true";
|
|
101166
101257
|
}
|
|
101167
|
-
function
|
|
101258
|
+
function isStrictMode(env = process.env) {
|
|
101259
|
+
return isEnvFlag(env, "CODERIFTS_STRICT");
|
|
101260
|
+
}
|
|
101261
|
+
function isAdvisoryMode(env = process.env) {
|
|
101262
|
+
return isEnvFlag(env, "CODERIFTS_ADVISORY");
|
|
101263
|
+
}
|
|
101264
|
+
function isMonitoringSinkWired({ env = process.env, deps = {}, result = null } = {}) {
|
|
101265
|
+
if (deps && deps.monitoringSinkWired === true) return true;
|
|
101266
|
+
if (isEnvFlag(env, "CODERIFTS_MONITORING_SINK_WIRED")) return true;
|
|
101267
|
+
const git = readGitConfig("coderifts.monitoringSinkWired", deps);
|
|
101268
|
+
if (git) {
|
|
101269
|
+
const s = String(git).trim().toLowerCase();
|
|
101270
|
+
if (s === "1" || s === "true") return true;
|
|
101271
|
+
}
|
|
101272
|
+
if (result && result.monitoringSinkWired === true) return true;
|
|
101273
|
+
const dr = result && result.decision_result;
|
|
101274
|
+
if (dr && typeof dr === "object" && dr.monitoringSinkWired === true) return true;
|
|
101275
|
+
return false;
|
|
101276
|
+
}
|
|
101277
|
+
var HOOK_TRIGGER_SOURCE = "claude_hook";
|
|
101278
|
+
function extractFingerprint(result) {
|
|
101279
|
+
const d = result && typeof result === "object" && !Array.isArray(result) ? result : {};
|
|
101280
|
+
const dr = d.decision_result && typeof d.decision_result === "object" ? d.decision_result : {};
|
|
101281
|
+
const fp = dr.fingerprint || d.fingerprint || d.verdict_fingerprint || d.input_fingerprint;
|
|
101282
|
+
return typeof fp === "string" && fp.length > 0 ? fp : void 0;
|
|
101283
|
+
}
|
|
101284
|
+
function hookSessionId(env) {
|
|
101285
|
+
const fromEnv = env && env.CODERIFTS_SESSION_ID && String(env.CODERIFTS_SESSION_ID).trim();
|
|
101286
|
+
if (fromEnv) return fromEnv;
|
|
101287
|
+
if (!hookSessionId._gen) hookSessionId._gen = `hook-${process.pid}-${Date.now()}`;
|
|
101288
|
+
return hookSessionId._gen;
|
|
101289
|
+
}
|
|
101290
|
+
function appendGuardEventLog(env, event, deps = {}) {
|
|
101291
|
+
const p = env && env.CODERIFTS_GUARD_EVENT_LOG;
|
|
101292
|
+
if (p == null || String(p).trim() === "") return;
|
|
101293
|
+
const errLog = deps.errLog || ((m) => console.error(String(m)));
|
|
101294
|
+
const append = deps.appendFileSync || ((file, data) => fs.appendFileSync(file, data));
|
|
101295
|
+
try {
|
|
101296
|
+
append(String(p).trim(), JSON.stringify(event) + "\n");
|
|
101297
|
+
} catch (err) {
|
|
101298
|
+
try {
|
|
101299
|
+
errLog(`CodeRifts claude-hook: CODERIFTS_GUARD_EVENT_LOG write failed (${err && err.message})`);
|
|
101300
|
+
} catch {
|
|
101301
|
+
}
|
|
101302
|
+
}
|
|
101303
|
+
}
|
|
101304
|
+
function failClosedOrAdvisory({ advisory, strict, site, softMsg, why, errLog }) {
|
|
101305
|
+
if (advisory && !strict) {
|
|
101306
|
+
errLog(`${softMsg} (CODERIFTS_ADVISORY)`);
|
|
101307
|
+
return { exitCode: 0, reason: site, site };
|
|
101308
|
+
}
|
|
101168
101309
|
if (strict) {
|
|
101169
101310
|
errLog(
|
|
101170
101311
|
`CodeRifts claude-hook: CODERIFTS_STRICT: governance could not run (${why}) \u2014 blocking. Set a key / restore network, or unset CODERIFTS_STRICT.`
|
|
101171
101312
|
);
|
|
101172
|
-
return { exitCode: 2, reason, strictBlocked: true };
|
|
101313
|
+
return { exitCode: 2, reason: "enforce_indeterminate", site, strictBlocked: true };
|
|
101173
101314
|
}
|
|
101174
|
-
errLog(
|
|
101175
|
-
|
|
101315
|
+
errLog(
|
|
101316
|
+
`CodeRifts claude-hook: governance could not run (${why}) \u2014 blocking. Set CODERIFTS_ADVISORY=1 to allow without governance (explicit opt-out).`
|
|
101317
|
+
);
|
|
101318
|
+
return { exitCode: 2, reason: "enforce_indeterminate", site };
|
|
101319
|
+
}
|
|
101320
|
+
function softOrStrictBlock({ strict, reason, softMsg, why, errLog }) {
|
|
101321
|
+
return failClosedOrAdvisory({
|
|
101322
|
+
advisory: false,
|
|
101323
|
+
strict: !!strict,
|
|
101324
|
+
site: reason,
|
|
101325
|
+
softMsg,
|
|
101326
|
+
why,
|
|
101327
|
+
errLog
|
|
101328
|
+
});
|
|
101176
101329
|
}
|
|
101177
101330
|
function readGitConfig(key, deps = {}) {
|
|
101178
101331
|
const run = deps.execSync || execSync;
|
|
@@ -101279,7 +101432,7 @@ var require_claude_hook = __commonJS({
|
|
|
101279
101432
|
return { ok: false, reason: `unsupported tool_name for content derive: ${name}` };
|
|
101280
101433
|
}
|
|
101281
101434
|
function mapDecisionSeverity(result) {
|
|
101282
|
-
const d = result && typeof result === "object" ? result : {};
|
|
101435
|
+
const d = result && typeof result === "object" && !Array.isArray(result) ? result : {};
|
|
101283
101436
|
let ea = null;
|
|
101284
101437
|
const dr = d.decision_result;
|
|
101285
101438
|
if (dr && typeof dr === "object" && typeof dr.execution_action === "string") {
|
|
@@ -101287,20 +101440,33 @@ var require_claude_hook = __commonJS({
|
|
|
101287
101440
|
} else if (typeof d.execution_action === "string") {
|
|
101288
101441
|
ea = d.execution_action;
|
|
101289
101442
|
}
|
|
101443
|
+
const hasDecision = d.omega_decision != null && d.omega_decision !== "" || d.decision != null && d.decision !== "" || dr && typeof dr === "object" && dr.execution_action;
|
|
101444
|
+
if (d.error && !hasDecision && (ea == null || ea === "")) {
|
|
101445
|
+
return {
|
|
101446
|
+
severity: "INDETERMINATE",
|
|
101447
|
+
decision: null,
|
|
101448
|
+
decisionId: null,
|
|
101449
|
+
executionAction: null
|
|
101450
|
+
};
|
|
101451
|
+
}
|
|
101290
101452
|
let severity;
|
|
101291
101453
|
if (ea != null && ea !== "") {
|
|
101292
101454
|
if (!CLOSED_ACTIONS.has(ea)) {
|
|
101293
101455
|
severity = "UNKNOWN";
|
|
101294
|
-
} else if (ea === "CONTINUE"
|
|
101456
|
+
} else if (ea === "CONTINUE") {
|
|
101295
101457
|
severity = "ALLOW";
|
|
101458
|
+
} else if (ea === "CONTINUE_WITH_MONITORING") {
|
|
101459
|
+
severity = "MONITOR";
|
|
101296
101460
|
} else if (ea === "STOP") {
|
|
101297
101461
|
severity = "BLOCK";
|
|
101298
101462
|
} else {
|
|
101299
101463
|
severity = "REQUIRE_APPROVAL";
|
|
101300
101464
|
}
|
|
101301
101465
|
} else {
|
|
101302
|
-
const od = d.omega_decision || d.decision
|
|
101303
|
-
if (od === "
|
|
101466
|
+
const od = d.omega_decision || d.decision;
|
|
101467
|
+
if (od == null || od === "") {
|
|
101468
|
+
severity = "INDETERMINATE";
|
|
101469
|
+
} else if (od === "BLOCK") severity = "BLOCK";
|
|
101304
101470
|
else if (od === "REQUIRE_APPROVAL") severity = "REQUIRE_APPROVAL";
|
|
101305
101471
|
else if (od === "WARN") severity = "WARN";
|
|
101306
101472
|
else severity = "ALLOW";
|
|
@@ -101309,7 +101475,7 @@ var require_claude_hook = __commonJS({
|
|
|
101309
101475
|
const decisionId = dr && dr.decision_id || d.decision_id || null;
|
|
101310
101476
|
return {
|
|
101311
101477
|
severity,
|
|
101312
|
-
decision: decision != null ? String(decision) : null,
|
|
101478
|
+
decision: decision != null && decision !== "" ? String(decision) : null,
|
|
101313
101479
|
decisionId: decisionId != null ? String(decisionId) : null,
|
|
101314
101480
|
executionAction: ea
|
|
101315
101481
|
};
|
|
@@ -101329,31 +101495,66 @@ var require_claude_hook = __commonJS({
|
|
|
101329
101495
|
const cwd = deps.cwd || process.cwd();
|
|
101330
101496
|
const env = deps.env || process.env;
|
|
101331
101497
|
const strict = isStrictMode(env);
|
|
101498
|
+
const advisory = isAdvisoryMode(env);
|
|
101499
|
+
const sessionId = hookSessionId(env);
|
|
101500
|
+
const logEv = (partial) => {
|
|
101501
|
+
appendGuardEventLog(env, {
|
|
101502
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
101503
|
+
sessionId,
|
|
101504
|
+
trigger_source: HOOK_TRIGGER_SOURCE,
|
|
101505
|
+
...partial
|
|
101506
|
+
}, { errLog, appendFileSync: deps.appendFileSync });
|
|
101507
|
+
};
|
|
101508
|
+
let tool = null;
|
|
101509
|
+
let filePathKnown = null;
|
|
101510
|
+
const emitTerminal = (r) => {
|
|
101511
|
+
const extra = {
|
|
101512
|
+
tool: tool || void 0,
|
|
101513
|
+
path: filePathKnown || void 0,
|
|
101514
|
+
decision: r.decision != null ? r.decision : void 0,
|
|
101515
|
+
decisionId: r.decisionId != null ? r.decisionId : void 0
|
|
101516
|
+
};
|
|
101517
|
+
if (r.exitCode === 2) {
|
|
101518
|
+
logEv({ type: "hook_blocked", exit: 2, ...extra, cause: r.reason || r.site });
|
|
101519
|
+
} else if (r.exitCode === 0 && r.site && advisory && !strict) {
|
|
101520
|
+
logEv({ type: "hook_advisory_passthrough", exit: 0, decision: r.decision || null, ...extra, cause: r.site });
|
|
101521
|
+
} else {
|
|
101522
|
+
logEv({ type: "hook_continue", exit: 0, ...extra, cause: r.reason });
|
|
101523
|
+
}
|
|
101524
|
+
return r;
|
|
101525
|
+
};
|
|
101332
101526
|
const apiKey = resolveApiKey({ ...deps, cwd, env });
|
|
101333
101527
|
if (!apiKey) {
|
|
101334
|
-
|
|
101528
|
+
logEv({ type: "preflight_unavailable", cause: "missing_api_key" });
|
|
101529
|
+
return emitTerminal(failClosedOrAdvisory({
|
|
101530
|
+
advisory,
|
|
101335
101531
|
strict,
|
|
101336
|
-
|
|
101532
|
+
site: "missing_api_key",
|
|
101337
101533
|
why: "no API key",
|
|
101338
101534
|
softMsg: "CodeRifts claude-hook: no API key (coderifts login / CODERIFTS_API_KEY / git config coderifts.apiKey) \u2014 allowing",
|
|
101339
101535
|
errLog
|
|
101340
|
-
});
|
|
101536
|
+
}));
|
|
101341
101537
|
}
|
|
101342
101538
|
const raw = typeof options.stdin === "string" ? options.stdin : readStdin();
|
|
101343
101539
|
const parsed = parseStdinJson(raw);
|
|
101344
101540
|
if (!parsed.ok) {
|
|
101345
101541
|
errLog(`CodeRifts claude-hook: ${parsed.reason} \u2014 allowing (soft; never block on parse gap)`);
|
|
101346
|
-
|
|
101542
|
+
logEv({ type: "detection_skip", signals: ["stdin_unparseable"] });
|
|
101543
|
+
return emitTerminal({ exitCode: 0, reason: "stdin_unparseable" });
|
|
101347
101544
|
}
|
|
101348
101545
|
const { toolName, toolInput } = parsed;
|
|
101546
|
+
tool = toolName;
|
|
101349
101547
|
const filePath = toolInput.file_path || toolInput.filePath || toolInput.path;
|
|
101350
101548
|
if (!filePath || typeof filePath !== "string") {
|
|
101351
101549
|
errLog("CodeRifts claude-hook: tool_input.file_path missing \u2014 allowing (soft)");
|
|
101352
|
-
|
|
101550
|
+
logEv({ type: "detection_skip", signals: ["missing_file_path"], tool });
|
|
101551
|
+
return emitTerminal({ exitCode: 0, reason: "missing_file_path" });
|
|
101353
101552
|
}
|
|
101553
|
+
filePathKnown = filePath;
|
|
101354
101554
|
const specPath = resolveSpecPath({ ...deps, cwd });
|
|
101355
101555
|
if (!isSpecPath(filePath, specPath)) {
|
|
101356
|
-
|
|
101556
|
+
logEv({ type: "detection_skip", signals: ["not_spec_path"], tool, path: filePath });
|
|
101557
|
+
return emitTerminal({ exitCode: 0, reason: "not_spec_path" });
|
|
101357
101558
|
}
|
|
101358
101559
|
const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
|
|
101359
101560
|
let diskBefore = "";
|
|
@@ -101361,28 +101562,34 @@ var require_claude_hook = __commonJS({
|
|
|
101361
101562
|
try {
|
|
101362
101563
|
diskBefore = readFile(absPath, "utf8");
|
|
101363
101564
|
} catch (e) {
|
|
101364
|
-
|
|
101565
|
+
logEv({ type: "preflight_unavailable", cause: "disk_unreadable", tool, path: filePath });
|
|
101566
|
+
return emitTerminal(failClosedOrAdvisory({
|
|
101567
|
+
advisory,
|
|
101365
101568
|
strict,
|
|
101366
|
-
|
|
101569
|
+
site: "disk_unreadable",
|
|
101367
101570
|
why: `cannot read contract file ${absPath}`,
|
|
101368
101571
|
softMsg: `CodeRifts claude-hook: cannot read ${absPath} \u2014 allowing (soft)`,
|
|
101369
101572
|
errLog
|
|
101370
|
-
});
|
|
101573
|
+
}));
|
|
101371
101574
|
}
|
|
101372
101575
|
}
|
|
101373
101576
|
const derived = deriveAfterContent(toolName, toolInput, diskBefore);
|
|
101374
101577
|
if (!derived.ok) {
|
|
101375
|
-
|
|
101578
|
+
logEv({ type: "preflight_unavailable", cause: "edit_apply_failed", tool, path: filePath });
|
|
101579
|
+
return emitTerminal(failClosedOrAdvisory({
|
|
101580
|
+
advisory,
|
|
101376
101581
|
strict,
|
|
101377
|
-
|
|
101582
|
+
site: "edit_apply_failed",
|
|
101378
101583
|
why: derived.reason,
|
|
101379
101584
|
softMsg: `CodeRifts claude-hook: ${derived.reason} \u2014 allowing (soft; never guess edit apply)`,
|
|
101380
101585
|
errLog
|
|
101381
|
-
});
|
|
101586
|
+
}));
|
|
101382
101587
|
}
|
|
101383
101588
|
if (diskBefore === derived.after) {
|
|
101384
|
-
|
|
101589
|
+
logEv({ type: "detection_skip", signals: ["identical"], tool, path: filePath });
|
|
101590
|
+
return emitTerminal({ exitCode: 0, reason: "identical" });
|
|
101385
101591
|
}
|
|
101592
|
+
logEv({ type: "preflight_start", tool, path: filePath });
|
|
101386
101593
|
let result;
|
|
101387
101594
|
try {
|
|
101388
101595
|
result = await authorize(diskBefore, derived.after, apiKey, {
|
|
@@ -101391,47 +101598,95 @@ var require_claude_hook = __commonJS({
|
|
|
101391
101598
|
});
|
|
101392
101599
|
} catch (e) {
|
|
101393
101600
|
const msg = e && e.message ? String(e.message) : "request failed";
|
|
101394
|
-
|
|
101601
|
+
logEv({ type: "preflight_unavailable", cause: "api_unreachable", tool, path: filePath });
|
|
101602
|
+
return emitTerminal(failClosedOrAdvisory({
|
|
101603
|
+
advisory,
|
|
101395
101604
|
strict,
|
|
101396
|
-
|
|
101605
|
+
site: "api_unreachable",
|
|
101397
101606
|
why: `API unreachable: ${msg}`,
|
|
101398
101607
|
softMsg: `CodeRifts claude-hook: API unreachable (${msg}) \u2014 allowing (soft; availability must not brick the editor)`,
|
|
101399
101608
|
errLog
|
|
101400
|
-
});
|
|
101609
|
+
}));
|
|
101401
101610
|
}
|
|
101402
101611
|
const mapped = mapDecisionSeverity(result);
|
|
101612
|
+
const fingerprint = extractFingerprint(result);
|
|
101403
101613
|
const idPart = mapped.decisionId ? ` decision_id=${mapped.decisionId}` : "";
|
|
101404
101614
|
const decPart = mapped.decision ? ` decision=${mapped.decision}` : "";
|
|
101405
101615
|
const eaPart = mapped.executionAction ? ` execution_action=${mapped.executionAction}` : "";
|
|
101616
|
+
const actionForLog = mapped.executionAction || (mapped.severity === "BLOCK" || mapped.severity === "UNKNOWN" || mapped.severity === "INDETERMINATE" ? "STOP" : mapped.severity === "REQUIRE_APPROVAL" ? "REQUEST_APPROVAL" : mapped.severity === "MONITOR" || mapped.severity === "WARN" ? "CONTINUE_WITH_MONITORING" : "CONTINUE");
|
|
101617
|
+
logEv({
|
|
101618
|
+
type: "preflight_result",
|
|
101619
|
+
action: actionForLog,
|
|
101620
|
+
decisionId: mapped.decisionId || void 0,
|
|
101621
|
+
fingerprint,
|
|
101622
|
+
tool,
|
|
101623
|
+
path: filePath
|
|
101624
|
+
});
|
|
101625
|
+
if (mapped.severity === "INDETERMINATE") {
|
|
101626
|
+
errLog(
|
|
101627
|
+
"CodeRifts claude-hook: BLOCKED (indeterminate response \u2014 no execution_action and no decision; not permission)"
|
|
101628
|
+
);
|
|
101629
|
+
return emitTerminal({
|
|
101630
|
+
exitCode: 2,
|
|
101631
|
+
reason: "indeterminate",
|
|
101632
|
+
severity: "INDETERMINATE",
|
|
101633
|
+
decision: mapped.decision,
|
|
101634
|
+
decisionId: mapped.decisionId
|
|
101635
|
+
});
|
|
101636
|
+
}
|
|
101406
101637
|
if (mapped.severity === "BLOCK" || mapped.severity === "UNKNOWN") {
|
|
101407
101638
|
errLog(
|
|
101408
101639
|
`CodeRifts claude-hook: BLOCKED${decPart}${eaPart}${idPart}` + (mapped.severity === "UNKNOWN" ? " (unrecognised execution_action)" : "")
|
|
101409
101640
|
);
|
|
101410
|
-
return {
|
|
101641
|
+
return emitTerminal({
|
|
101411
101642
|
exitCode: 2,
|
|
101412
101643
|
reason: mapped.severity === "UNKNOWN" ? "unknown_action" : "block",
|
|
101413
101644
|
severity: mapped.severity,
|
|
101414
101645
|
decision: mapped.decision,
|
|
101415
101646
|
decisionId: mapped.decisionId
|
|
101416
|
-
};
|
|
101647
|
+
});
|
|
101417
101648
|
}
|
|
101418
|
-
if (mapped.severity === "REQUIRE_APPROVAL"
|
|
101419
|
-
errLog(
|
|
101420
|
-
|
|
101421
|
-
|
|
101422
|
-
|
|
101649
|
+
if (mapped.severity === "REQUIRE_APPROVAL") {
|
|
101650
|
+
errLog(
|
|
101651
|
+
`CodeRifts claude-hook: BLOCKED approval_required${decPart}${eaPart}${idPart}`
|
|
101652
|
+
);
|
|
101653
|
+
return emitTerminal({
|
|
101654
|
+
exitCode: 2,
|
|
101655
|
+
reason: "approval_required",
|
|
101423
101656
|
severity: mapped.severity,
|
|
101424
101657
|
decision: mapped.decision,
|
|
101425
101658
|
decisionId: mapped.decisionId
|
|
101426
|
-
};
|
|
101659
|
+
});
|
|
101427
101660
|
}
|
|
101428
|
-
|
|
101661
|
+
if (mapped.severity === "MONITOR" || mapped.severity === "WARN") {
|
|
101662
|
+
const sinkWired = isMonitoringSinkWired({ env, deps, result });
|
|
101663
|
+
if (!sinkWired) {
|
|
101664
|
+
errLog(
|
|
101665
|
+
`CodeRifts claude-hook: BLOCKED monitoring_unwired${decPart}${eaPart}${idPart} \u2014 CONTINUE_WITH_MONITORING requires CODERIFTS_MONITORING_SINK_WIRED=1 (host assertion; not delivery proof)`
|
|
101666
|
+
);
|
|
101667
|
+
return emitTerminal({
|
|
101668
|
+
exitCode: 2,
|
|
101669
|
+
reason: "monitoring_unwired",
|
|
101670
|
+
severity: mapped.severity === "WARN" ? "MONITOR" : mapped.severity,
|
|
101671
|
+
decision: mapped.decision,
|
|
101672
|
+
decisionId: mapped.decisionId
|
|
101673
|
+
});
|
|
101674
|
+
}
|
|
101675
|
+
return emitTerminal({
|
|
101676
|
+
exitCode: 0,
|
|
101677
|
+
reason: "allow",
|
|
101678
|
+
severity: mapped.severity === "WARN" ? "MONITOR" : mapped.severity,
|
|
101679
|
+
decision: mapped.decision,
|
|
101680
|
+
decisionId: mapped.decisionId
|
|
101681
|
+
});
|
|
101682
|
+
}
|
|
101683
|
+
return emitTerminal({
|
|
101429
101684
|
exitCode: 0,
|
|
101430
101685
|
reason: "allow",
|
|
101431
101686
|
severity: "ALLOW",
|
|
101432
101687
|
decision: mapped.decision,
|
|
101433
101688
|
decisionId: mapped.decisionId
|
|
101434
|
-
};
|
|
101689
|
+
});
|
|
101435
101690
|
}
|
|
101436
101691
|
module2.exports = {
|
|
101437
101692
|
runClaudeHook,
|
|
@@ -101442,11 +101697,18 @@ var require_claude_hook = __commonJS({
|
|
|
101442
101697
|
resolveApiKey,
|
|
101443
101698
|
resolveSpecPath,
|
|
101444
101699
|
readGitConfig,
|
|
101700
|
+
isEnvFlag,
|
|
101445
101701
|
isStrictMode,
|
|
101702
|
+
isAdvisoryMode,
|
|
101703
|
+
isMonitoringSinkWired,
|
|
101704
|
+
failClosedOrAdvisory,
|
|
101446
101705
|
softOrStrictBlock,
|
|
101447
101706
|
DEFAULT_SPEC_PATH,
|
|
101448
101707
|
CLOSED_ACTIONS,
|
|
101449
|
-
USAGE
|
|
101708
|
+
USAGE,
|
|
101709
|
+
appendGuardEventLog,
|
|
101710
|
+
extractFingerprint,
|
|
101711
|
+
HOOK_TRIGGER_SOURCE
|
|
101450
101712
|
};
|
|
101451
101713
|
}
|
|
101452
101714
|
});
|
|
@@ -103446,7 +103708,7 @@ ${USAGE}
|
|
|
103446
103708
|
}
|
|
103447
103709
|
});
|
|
103448
103710
|
program.command("claude-hook").description(
|
|
103449
|
-
"Claude Code PreToolUse gate: block contract-touching Write|Edit|MultiEdit on BLOCK/STOP (exit 2).
|
|
103711
|
+
"Claude Code PreToolUse gate: block contract-touching Write|Edit|MultiEdit on BLOCK/STOP/REQUEST_APPROVAL (exit 2). Fail-closed when governance cannot run; CODERIFTS_ADVISORY=1 is the explicit soft opt-out."
|
|
103450
103712
|
).addHelpText("after", () => {
|
|
103451
103713
|
const { USAGE } = require_claude_hook();
|
|
103452
103714
|
return `
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coderifts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Detect breaking API changes from the command line. Works locally or with the CodeRifts cloud API.",
|
|
5
5
|
"author": "CodeRifts <hello@coderifts.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"bin/",
|
|
14
14
|
"scripts/",
|
|
15
15
|
"README.md",
|
|
16
|
+
"CHANGELOG.md",
|
|
16
17
|
"corpus/"
|
|
17
18
|
],
|
|
18
19
|
"keywords": [
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
"test": "node --test test/*.test.js"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
|
-
"@coderifts/agent-guard": "^
|
|
48
|
+
"@coderifts/agent-guard": "^8.1.1",
|
|
48
49
|
"chalk": "^4.1.2",
|
|
49
50
|
"cli-table3": "^0.6.4",
|
|
50
51
|
"commander": "^12.0.0",
|
|
@@ -5,18 +5,19 @@
|
|
|
5
5
|
* CLI release gate (prepublishOnly): refuse to publish if the declared
|
|
6
6
|
* @coderifts/agent-guard range floors below the minimum acceptable major.
|
|
7
7
|
*
|
|
8
|
-
* Honest scope: compares the dependency *range floor* (e.g. ^
|
|
8
|
+
* Honest scope: compares the dependency *range floor* (e.g. ^8.1.1 → 8) to a
|
|
9
9
|
* pinned constant. Does not call npm registry. Optional: if node_modules has a
|
|
10
10
|
* resolved package, also fail when its major is below the floor (stale install).
|
|
11
11
|
*
|
|
12
12
|
* Why: evening audit 2.4 — CLI sat on ^1.6.0 while published guard is 6.x.
|
|
13
|
+
* 4.0.0: floor raised to 8 (deployGate inescapable_deploy includes fingerprint rebound).
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
const fs = require('fs');
|
|
16
17
|
const path = require('path');
|
|
17
18
|
|
|
18
19
|
/** Minimum acceptable major for @coderifts/agent-guard (published line). */
|
|
19
|
-
const MIN_GUARD_MAJOR =
|
|
20
|
+
const MIN_GUARD_MAJOR = 8;
|
|
20
21
|
const DEP = '@coderifts/agent-guard';
|
|
21
22
|
|
|
22
23
|
function fail(msg) {
|