instar 1.2.69 → 1.2.70
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/dist/core/installCodexHooks.d.ts.map +1 -1
- package/dist/core/installCodexHooks.js +7 -2
- package/dist/core/installCodexHooks.js.map +1 -1
- package/dist/providers/adapters/openai-codex/canary/codexHookContractCanary.d.ts +65 -0
- package/dist/providers/adapters/openai-codex/canary/codexHookContractCanary.d.ts.map +1 -0
- package/dist/providers/adapters/openai-codex/canary/codexHookContractCanary.js +178 -0
- package/dist/providers/adapters/openai-codex/canary/codexHookContractCanary.js.map +1 -0
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.2.70.md +72 -0
- package/upgrades/side-effects/codex-parity-canary-scope-coherence.md +34 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installCodexHooks.d.ts","sourceRoot":"","sources":["../../src/core/installCodexHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAE/D,UAAU,gBAAgB;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAMD,2FAA2F;AAC3F,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"installCodexHooks.d.ts","sourceRoot":"","sources":["../../src/core/installCodexHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAE/D,UAAU,gBAAgB;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAMD,2FAA2F;AAC3F,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAiDlC;AAQD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAwB5D"}
|
|
@@ -66,9 +66,14 @@ export function buildInstarCodexHookGroups(projectDir) {
|
|
|
66
66
|
PermissionRequest: [
|
|
67
67
|
{ matcher: '.*', hooks: [node('external-operation-gate.js')] },
|
|
68
68
|
],
|
|
69
|
-
// End-of-turn review: coherence/tone + deferral
|
|
69
|
+
// End-of-turn review: coherence/tone + deferral + scope-coherence checkpoint.
|
|
70
|
+
// All three are framework-neutral (read stdin, POST to the local server). Codex
|
|
71
|
+
// honors `{decision:"block", reason}` on Stop (verified in the 0.133 binary's
|
|
72
|
+
// StopCommandOutputWire) — the same grounding-pause semantics as Claude, NOT a
|
|
73
|
+
// hard termination. scope-coherence defaults to `approve` and self-throttles
|
|
74
|
+
// (depth threshold + 30-min cooldown), so it can't loop an autonomous Codex run.
|
|
70
75
|
Stop: [
|
|
71
|
-
{ matcher: '', hooks: [{ ...node('response-review.js'), timeout: 10000 }, node('deferral-detector.js')] },
|
|
76
|
+
{ matcher: '', hooks: [{ ...node('response-review.js'), timeout: 10000 }, node('deferral-detector.js'), node('scope-coherence-checkpoint.js')] },
|
|
72
77
|
],
|
|
73
78
|
// Identity/context injection.
|
|
74
79
|
SessionStart: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installCodexHooks.js","sourceRoot":"","sources":["../../src/core/installCodexHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,oFAAoF;AACpF,MAAM,CAAC,MAAM,uBAAuB,GAAG,uBAAuB,CAAC;AAgB/D,2FAA2F;AAC3F,MAAM,UAAU,0BAA0B,CACxC,UAAkB;IAElB,MAAM,IAAI,GAAG,CAAC,MAAc,EAAoB,EAAE,CAAC,CAAC;QAClD,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QACzE,OAAO,EAAE,IAAI;KACd,CAAC,CAAC;IACH,MAAM,EAAE,GAAG,CAAC,MAAc,EAAoB,EAAE,CAAC,CAAC;QAChD,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QACzE,OAAO,EAAE,IAAI;KACd,CAAC,CAAC;IAEH,OAAO;QACL,8EAA8E;QAC9E,0EAA0E;QAC1E,wEAAwE;QACxE,gFAAgF;QAChF,gFAAgF;QAChF,6EAA6E;QAC7E,oFAAoF;QACpF,gFAAgF;QAChF,gFAAgF;QAChF,kFAAkF;QAClF,2BAA2B;QAC3B,UAAU,EAAE;YACV,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,4BAA4B,CAAC,EAAE,IAAI,CAAC,4BAA4B,CAAC,EAAE,EAAE,CAAC,+BAA+B,CAAC,CAAC,EAAE;SACtI;QACD,mEAAmE;QACnE,2EAA2E;QAC3E,iBAAiB,EAAE;YACjB,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,EAAE;SAC/D;QACD,
|
|
1
|
+
{"version":3,"file":"installCodexHooks.js","sourceRoot":"","sources":["../../src/core/installCodexHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex hook-contract canary.
|
|
3
|
+
*
|
|
4
|
+
* The Codex enforcement layer (installCodexHooks → .codex/hooks.json) rests on
|
|
5
|
+
* a handful of load-bearing, empirically-won assumptions about Codex's hook
|
|
6
|
+
* contract. Two of them were silent-failure bugs we already paid for live
|
|
7
|
+
* (see docs/specs/codex-enforcement-hook-layer.md §P5c):
|
|
8
|
+
* 1. The PreToolUse `matcher` is a REGEX against the tool name — a bare `*`
|
|
9
|
+
* matches NOTHING, so the guard silently never fires. It must be `.*`.
|
|
10
|
+
* 2. Codex's shell tool is `exec_command` and carries the command in
|
|
11
|
+
* `tool_input.cmd` (not Claude's `tool_input.command`); the guard shim
|
|
12
|
+
* reads both.
|
|
13
|
+
* If a future Codex version renames the hook events, drops PreToolUse, or
|
|
14
|
+
* changes the deny mechanism, the gates would silently no-op again — "looks
|
|
15
|
+
* installed, blocks nothing." This canary fails loudly instead.
|
|
16
|
+
*
|
|
17
|
+
* Two layers:
|
|
18
|
+
* (A) DETERMINISTIC invariant lock (always runs, env-independent): asserts
|
|
19
|
+
* buildInstarCodexHookGroups still emits the load-bearing shape — `.*`
|
|
20
|
+
* matcher, dangerous-command-guard on PreToolUse, the full Stop review
|
|
21
|
+
* trio. A refactor that regresses any of these fails the canary in CI.
|
|
22
|
+
* (B) BEST-EFFORT live-binary contract check: if a codex binary is
|
|
23
|
+
* resolvable on this host, read its embedded hook-event schema and
|
|
24
|
+
* assert the events instar depends on (PreToolUse, PermissionRequest,
|
|
25
|
+
* Stop, SessionStart, UserPromptSubmit) are still present. No binary →
|
|
26
|
+
* status 'skip' for layer B (NOT fail — most hosts/CI have no codex).
|
|
27
|
+
*
|
|
28
|
+
* RULE 3.1 RATIONALE
|
|
29
|
+
* Criticality: critical — a silent no-op guard is a false sense of safety on
|
|
30
|
+
* Codex's main destructive surface (shell/exec).
|
|
31
|
+
* Frequency: startup/CI canary (deterministic layer is a unit-test drift lock;
|
|
32
|
+
* binary layer is best-effort per host).
|
|
33
|
+
* Stability: semi-stable — Codex changes hook vocabulary across minor versions.
|
|
34
|
+
* Fallback: none — failure is a code-fix surface, surfaced via DegradationReporter.
|
|
35
|
+
* Verdict: deterministic structural assertion (A) + version-tolerant
|
|
36
|
+
* binary-schema probe (B); no LLM fallback needed.
|
|
37
|
+
*/
|
|
38
|
+
/** Hook events instar's enforcement layer registers and depends on. */
|
|
39
|
+
export declare const REQUIRED_CODEX_HOOK_EVENTS: readonly ["PreToolUse", "PermissionRequest", "Stop", "SessionStart", "UserPromptSubmit"];
|
|
40
|
+
export interface CodexHookContractCanaryResult {
|
|
41
|
+
status: 'pass' | 'fail' | 'skip';
|
|
42
|
+
message: string;
|
|
43
|
+
details: {
|
|
44
|
+
/** Layer A — deterministic invariant lock. */
|
|
45
|
+
matcherIsRegex: boolean;
|
|
46
|
+
dangerousGuardOnPreToolUse: boolean;
|
|
47
|
+
stopReviewTrioWired: boolean;
|
|
48
|
+
/** Layer B — live-binary probe. */
|
|
49
|
+
binaryProbed: boolean;
|
|
50
|
+
binaryPath?: string;
|
|
51
|
+
missingEventsInBinary: string[];
|
|
52
|
+
failures: string[];
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve a codex binary path, best-effort. Returns null if none found —
|
|
57
|
+
* the binary layer is then skipped, not failed.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveCodexBinaryForCanary(): string | null;
|
|
60
|
+
/**
|
|
61
|
+
* Run the canary. Layer A is synchronous and always meaningful. Layer B
|
|
62
|
+
* probes a codex binary if one resolves; otherwise that layer is skipped.
|
|
63
|
+
*/
|
|
64
|
+
export declare function runCodexHookContractCanary(): CodexHookContractCanaryResult;
|
|
65
|
+
//# sourceMappingURL=codexHookContractCanary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codexHookContractCanary.d.ts","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/canary/codexHookContractCanary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAQH,uEAAuE;AACvE,eAAO,MAAM,0BAA0B,0FAM7B,CAAC;AAEX,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QACP,8CAA8C;QAC9C,cAAc,EAAE,OAAO,CAAC;QACxB,0BAA0B,EAAE,OAAO,CAAC;QACpC,mBAAmB,EAAE,OAAO,CAAC;QAC7B,mCAAmC;QACnC,YAAY,EAAE,OAAO,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,qBAAqB,EAAE,MAAM,EAAE,CAAC;QAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH;AAID;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,GAAG,IAAI,CAqB3D;AAkBD;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,6BAA6B,CAqF1E"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex hook-contract canary.
|
|
3
|
+
*
|
|
4
|
+
* The Codex enforcement layer (installCodexHooks → .codex/hooks.json) rests on
|
|
5
|
+
* a handful of load-bearing, empirically-won assumptions about Codex's hook
|
|
6
|
+
* contract. Two of them were silent-failure bugs we already paid for live
|
|
7
|
+
* (see docs/specs/codex-enforcement-hook-layer.md §P5c):
|
|
8
|
+
* 1. The PreToolUse `matcher` is a REGEX against the tool name — a bare `*`
|
|
9
|
+
* matches NOTHING, so the guard silently never fires. It must be `.*`.
|
|
10
|
+
* 2. Codex's shell tool is `exec_command` and carries the command in
|
|
11
|
+
* `tool_input.cmd` (not Claude's `tool_input.command`); the guard shim
|
|
12
|
+
* reads both.
|
|
13
|
+
* If a future Codex version renames the hook events, drops PreToolUse, or
|
|
14
|
+
* changes the deny mechanism, the gates would silently no-op again — "looks
|
|
15
|
+
* installed, blocks nothing." This canary fails loudly instead.
|
|
16
|
+
*
|
|
17
|
+
* Two layers:
|
|
18
|
+
* (A) DETERMINISTIC invariant lock (always runs, env-independent): asserts
|
|
19
|
+
* buildInstarCodexHookGroups still emits the load-bearing shape — `.*`
|
|
20
|
+
* matcher, dangerous-command-guard on PreToolUse, the full Stop review
|
|
21
|
+
* trio. A refactor that regresses any of these fails the canary in CI.
|
|
22
|
+
* (B) BEST-EFFORT live-binary contract check: if a codex binary is
|
|
23
|
+
* resolvable on this host, read its embedded hook-event schema and
|
|
24
|
+
* assert the events instar depends on (PreToolUse, PermissionRequest,
|
|
25
|
+
* Stop, SessionStart, UserPromptSubmit) are still present. No binary →
|
|
26
|
+
* status 'skip' for layer B (NOT fail — most hosts/CI have no codex).
|
|
27
|
+
*
|
|
28
|
+
* RULE 3.1 RATIONALE
|
|
29
|
+
* Criticality: critical — a silent no-op guard is a false sense of safety on
|
|
30
|
+
* Codex's main destructive surface (shell/exec).
|
|
31
|
+
* Frequency: startup/CI canary (deterministic layer is a unit-test drift lock;
|
|
32
|
+
* binary layer is best-effort per host).
|
|
33
|
+
* Stability: semi-stable — Codex changes hook vocabulary across minor versions.
|
|
34
|
+
* Fallback: none — failure is a code-fix surface, surfaced via DegradationReporter.
|
|
35
|
+
* Verdict: deterministic structural assertion (A) + version-tolerant
|
|
36
|
+
* binary-schema probe (B); no LLM fallback needed.
|
|
37
|
+
*/
|
|
38
|
+
import fs from 'node:fs';
|
|
39
|
+
import path from 'node:path';
|
|
40
|
+
import os from 'node:os';
|
|
41
|
+
import { execFileSync } from 'node:child_process';
|
|
42
|
+
import { buildInstarCodexHookGroups } from '../../../../core/installCodexHooks.js';
|
|
43
|
+
/** Hook events instar's enforcement layer registers and depends on. */
|
|
44
|
+
export const REQUIRED_CODEX_HOOK_EVENTS = [
|
|
45
|
+
'PreToolUse',
|
|
46
|
+
'PermissionRequest',
|
|
47
|
+
'Stop',
|
|
48
|
+
'SessionStart',
|
|
49
|
+
'UserPromptSubmit',
|
|
50
|
+
];
|
|
51
|
+
const PROBE_PROJECT_DIR = '/tmp/__codex_hook_canary__';
|
|
52
|
+
/**
|
|
53
|
+
* Resolve a codex binary path, best-effort. Returns null if none found —
|
|
54
|
+
* the binary layer is then skipped, not failed.
|
|
55
|
+
*/
|
|
56
|
+
export function resolveCodexBinaryForCanary() {
|
|
57
|
+
// 1. asdf shim / PATH lookup (the host's real, doc-matching codex).
|
|
58
|
+
for (const probe of ['codex']) {
|
|
59
|
+
try {
|
|
60
|
+
const out = execFileSync('which', [probe], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
61
|
+
if (out && fs.existsSync(out))
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
/* not on PATH */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// 2. Known npm-global vendor locations (asdf node installs).
|
|
69
|
+
const home = os.homedir();
|
|
70
|
+
const candidates = [
|
|
71
|
+
'/usr/local/bin/codex',
|
|
72
|
+
'/opt/homebrew/bin/codex',
|
|
73
|
+
path.join(home, '.asdf', 'shims', 'codex'),
|
|
74
|
+
];
|
|
75
|
+
for (const c of candidates) {
|
|
76
|
+
if (fs.existsSync(c))
|
|
77
|
+
return c;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Read a (possibly large) binary and test whether its embedded hook-event
|
|
83
|
+
* schema enumerates the given event name. We look for the quoted event name
|
|
84
|
+
* adjacent to the schema's hook-event vocabulary rather than anywhere in the
|
|
85
|
+
* file, to avoid coincidental matches. Tolerant: returns true on match.
|
|
86
|
+
*/
|
|
87
|
+
function binaryDeclaresEvent(binaryText, event) {
|
|
88
|
+
// The schema serializes events as `"const": "PreToolUse"` (HookSpecificOutput
|
|
89
|
+
// wires) and as enum members `"PreToolUse"` inside HookEventNameWire. Either
|
|
90
|
+
// form proves the event is part of the contract.
|
|
91
|
+
return (binaryText.includes(`"const": "${event}"`) ||
|
|
92
|
+
binaryText.includes(`"${event}"`));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Run the canary. Layer A is synchronous and always meaningful. Layer B
|
|
96
|
+
* probes a codex binary if one resolves; otherwise that layer is skipped.
|
|
97
|
+
*/
|
|
98
|
+
export function runCodexHookContractCanary() {
|
|
99
|
+
const failures = [];
|
|
100
|
+
// ---- Layer A: deterministic invariant lock ----
|
|
101
|
+
const groups = buildInstarCodexHookGroups(PROBE_PROJECT_DIR);
|
|
102
|
+
const preMatcher = groups.PreToolUse?.[0]?.matcher;
|
|
103
|
+
const matcherIsRegex = preMatcher === '.*';
|
|
104
|
+
if (!matcherIsRegex) {
|
|
105
|
+
failures.push(`PreToolUse matcher is '${preMatcher}', expected '.*' (a bare '*' matches nothing — gate would silently never fire)`);
|
|
106
|
+
}
|
|
107
|
+
const preCommands = (groups.PreToolUse?.[0]?.hooks ?? []).map((h) => h.command);
|
|
108
|
+
const dangerousGuardOnPreToolUse = preCommands.some((c) => c.includes('dangerous-command-guard.sh'));
|
|
109
|
+
if (!dangerousGuardOnPreToolUse) {
|
|
110
|
+
failures.push('dangerous-command-guard.sh missing from PreToolUse — Codex shell/exec would pass ungated');
|
|
111
|
+
}
|
|
112
|
+
const stopCommands = (groups.Stop?.[0]?.hooks ?? []).map((h) => h.command);
|
|
113
|
+
const stopReviewTrioWired = stopCommands.some((c) => c.includes('response-review.js')) &&
|
|
114
|
+
stopCommands.some((c) => c.includes('deferral-detector.js')) &&
|
|
115
|
+
stopCommands.some((c) => c.includes('scope-coherence-checkpoint.js'));
|
|
116
|
+
if (!stopReviewTrioWired) {
|
|
117
|
+
failures.push('Stop review trio incomplete — expected response-review + deferral-detector + scope-coherence-checkpoint');
|
|
118
|
+
}
|
|
119
|
+
// ---- Layer B: best-effort live-binary contract probe ----
|
|
120
|
+
const binaryPath = resolveCodexBinaryForCanary();
|
|
121
|
+
let binaryProbed = false;
|
|
122
|
+
const missingEventsInBinary = [];
|
|
123
|
+
if (binaryPath) {
|
|
124
|
+
try {
|
|
125
|
+
const text = fs.readFileSync(binaryPath, 'latin1');
|
|
126
|
+
// Sanity: only treat it as a probe if the binary actually carries the
|
|
127
|
+
// hook schema (resolves the "wrong binary" / shim-script case gracefully).
|
|
128
|
+
const looksLikeCodexHooks = text.includes('hook_event_name') || text.includes('HookEventNameWire');
|
|
129
|
+
if (looksLikeCodexHooks) {
|
|
130
|
+
binaryProbed = true;
|
|
131
|
+
for (const ev of REQUIRED_CODEX_HOOK_EVENTS) {
|
|
132
|
+
if (!binaryDeclaresEvent(text, ev))
|
|
133
|
+
missingEventsInBinary.push(ev);
|
|
134
|
+
}
|
|
135
|
+
if (missingEventsInBinary.length > 0) {
|
|
136
|
+
failures.push(`codex binary (${binaryPath}) no longer declares hook events: ${missingEventsInBinary.join(', ')} — the enforcement layer would not fire`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// If it doesn't look like a hooks-capable codex (e.g. a shim wrapper or
|
|
140
|
+
// an old version), leave binaryProbed=false → layer B skipped, not failed.
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Unreadable binary → skip layer B rather than fail.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const layerASolid = matcherIsRegex && dangerousGuardOnPreToolUse && stopReviewTrioWired;
|
|
147
|
+
let status;
|
|
148
|
+
let message;
|
|
149
|
+
if (failures.length > 0) {
|
|
150
|
+
status = 'fail';
|
|
151
|
+
message = `codex hook-contract canary: FAILED — ${failures.join('; ')}`;
|
|
152
|
+
}
|
|
153
|
+
else if (!binaryProbed) {
|
|
154
|
+
// Layer A passed but no codex binary to confirm the live contract.
|
|
155
|
+
status = 'skip';
|
|
156
|
+
message = layerASolid
|
|
157
|
+
? 'codex hook-contract canary: invariants intact (no codex binary on host — live-contract probe skipped)'
|
|
158
|
+
: 'codex hook-contract canary: invariants intact';
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
status = 'pass';
|
|
162
|
+
message = `codex hook-contract canary: invariants intact + binary (${binaryPath}) declares all required hook events`;
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
status,
|
|
166
|
+
message,
|
|
167
|
+
details: {
|
|
168
|
+
matcherIsRegex,
|
|
169
|
+
dangerousGuardOnPreToolUse,
|
|
170
|
+
stopReviewTrioWired,
|
|
171
|
+
binaryProbed,
|
|
172
|
+
binaryPath: binaryPath ?? undefined,
|
|
173
|
+
missingEventsInBinary,
|
|
174
|
+
failures,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=codexHookContractCanary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codexHookContractCanary.js","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/canary/codexHookContractCanary.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-05-25T03:
|
|
5
|
-
"instarVersion": "1.2.
|
|
4
|
+
"generatedAt": "2026-05-25T03:32:30.612Z",
|
|
5
|
+
"instarVersion": "1.2.70",
|
|
6
6
|
"entryCount": 191,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
<!-- patch = bug fixes, refactors, test additions, doc updates -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Two pieces of Codex-parity hardening on the enforcement-hook layer, both within
|
|
9
|
+
the approved spec (`docs/specs/codex-enforcement-hook-layer.md`):
|
|
10
|
+
|
|
11
|
+
1. **Scope-coherence checkpoint now runs on Codex.** `installCodexHooks` wires
|
|
12
|
+
`scope-coherence-checkpoint.js` into Codex's `Stop` event, joining the
|
|
13
|
+
`response-review` + `deferral-detector` pair already there. This completes the
|
|
14
|
+
spec §4.1 Stop mapping ("deferral / scope checkpoint → Stop") — previously only
|
|
15
|
+
deferral was wired. The script is framework-neutral (reads stdin, POSTs to the
|
|
16
|
+
local server) and Codex honors `{decision:"block", reason}` on `Stop` (verified
|
|
17
|
+
in the 0.133 binary's `StopCommandOutputWire`), so it gives Codex agents the same
|
|
18
|
+
structural "zoom out and re-read scope" grounding pause Claude agents get — not a
|
|
19
|
+
hard termination. It defaults to approve and self-throttles (depth threshold +
|
|
20
|
+
30-minute cooldown), so it cannot loop an autonomous run. Existing Codex agents
|
|
21
|
+
pick it up on update: the script already ships via always-overwrite migration and
|
|
22
|
+
`migrateHooks` re-runs `installCodexHooks` for codex-cli agents.
|
|
23
|
+
|
|
24
|
+
2. **A hook-contract drift canary** (`codexHookContractCanary.ts`). Layer A is an
|
|
25
|
+
env-independent invariant lock: it asserts the Codex hook config still has the
|
|
26
|
+
load-bearing shape that two earlier live silent-no-op bugs taught us to protect —
|
|
27
|
+
the `.*` tool matcher (a bare `*` matches nothing), `dangerous-command-guard` on
|
|
28
|
+
PreToolUse, and the full Stop review trio. A refactor that regresses any of these
|
|
29
|
+
fails CI. Layer B is best-effort: when a real codex binary is resolvable, it reads
|
|
30
|
+
the binary's embedded hook-event schema and confirms the events instar depends on
|
|
31
|
+
are still declared (catching real Codex-side contract drift). No binary present →
|
|
32
|
+
the binary layer skips rather than fails.
|
|
33
|
+
|
|
34
|
+
Also recorded honestly: a WIP that would have wired compaction-recovery to Codex's
|
|
35
|
+
`PostCompact` event was set aside after verifying against the 0.133 binary schema
|
|
36
|
+
that `PostCompact` has no `additionalContext` field — the only channel that
|
|
37
|
+
re-injects context into the model. It would have installed a hook that does nothing.
|
|
38
|
+
Codex compaction-recovery parity needs a different mechanism and is tracked.
|
|
39
|
+
|
|
40
|
+
## What to Tell Your User
|
|
41
|
+
|
|
42
|
+
- **Codex agents now get the same scope-grounding check Claude agents have**: "When
|
|
43
|
+
I've been heads-down implementing for a long stretch, I now get a structural nudge
|
|
44
|
+
to step back and re-check I'm building the right thing — on the Codex engine too,
|
|
45
|
+
not just on Claude."
|
|
46
|
+
- **A watchdog for the Codex safety guards**: "There's now an automatic check that
|
|
47
|
+
notices if the Codex safety guards ever stop firing or if Codex changes its format
|
|
48
|
+
underneath us — so a guard can't silently turn into a no-op without us catching it."
|
|
49
|
+
- Nothing for you to do — both ship automatically on update.
|
|
50
|
+
|
|
51
|
+
## Summary of New Capabilities
|
|
52
|
+
|
|
53
|
+
| Capability | How to Use |
|
|
54
|
+
|-----------|-----------|
|
|
55
|
+
| Scope-coherence checkpoint on Codex Stop | Automatic (installed via init + update migration) |
|
|
56
|
+
| Codex hook-contract drift canary | Automatic (CI invariant lock; best-effort binary probe) |
|
|
57
|
+
|
|
58
|
+
## Evidence
|
|
59
|
+
|
|
60
|
+
- **Codex Stop schema honors `decision:block`**: verified directly against the
|
|
61
|
+
codex-cli 0.133.0 binary — `strings` shows `StopCommandOutputWire` plus the error
|
|
62
|
+
string `"Stop hook returned decision:block without a non-empty reason"`, confirming
|
|
63
|
+
the block-with-reason contract the scope-coherence script relies on.
|
|
64
|
+
- **PostCompact cannot re-inject context** (why that WIP was dropped): the binary's
|
|
65
|
+
`post-compact.command.output` schema enumerates only `continue/stopReason/`
|
|
66
|
+
`suppressOutput/systemMessage` — no `additionalContext`. Only the `SessionStart`
|
|
67
|
+
and `UserPromptSubmit` output wires carry `additionalContext`, and `SessionStart`
|
|
68
|
+
triggers are `startup/resume/clear` (no `compact`). Verified by extracting the
|
|
69
|
+
embedded JSON schema from the binary.
|
|
70
|
+
- **Tests**: `installCodexHooks.test.ts` 8 green (incl. new Stop-trio assertion);
|
|
71
|
+
`codexHookContractCanary.test.ts` 6 green (layer-A invariants always asserted;
|
|
72
|
+
layer-B skip-not-fail with no binary). `tsc` clean.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Side-Effects Review: Codex parity — Stop scope-coherence + hook-contract canary (P6)
|
|
2
|
+
|
|
3
|
+
## Change
|
|
4
|
+
Two changes to the Codex enforcement layer, both within the approved spec (`docs/specs/codex-enforcement-hook-layer.md`):
|
|
5
|
+
|
|
6
|
+
1. **`installCodexHooks.ts`** — added `scope-coherence-checkpoint.js` to the Codex `Stop` event, alongside the existing `response-review.js` + `deferral-detector.js`. Completes the spec §4.1 mapping ("deferral / scope checkpoint → Stop"), which previously wired only deferral.
|
|
7
|
+
2. **`codexHookContractCanary.ts`** (new) — the P6 hook-contract drift canary. Two layers: (A) a deterministic, env-independent invariant lock asserting `buildInstarCodexHookGroups` still emits the load-bearing shape (`.*` matcher, `dangerous-command-guard` on PreToolUse, the full Stop review trio); (B) a best-effort probe of a resolvable codex binary's embedded hook-event schema, asserting the events instar depends on are still declared.
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
- **Scope-coherence on Stop**: the spec intends it; without it, Codex agents drift deep into implementation without the structural zoom-out that Claude agents get. The script is framework-neutral (reads stdin, POSTs to the local server) and Codex honors `{decision:'block', reason}` on Stop (verified in the 0.133 binary's `StopCommandOutputWire`). Same grounding-pause semantics as Claude — not a hard termination.
|
|
11
|
+
- **Canary**: P5c paid for two silent-no-op bugs live (a `*` matcher that matched nothing; the `cmd` vs `command` field). Layer A is the regression lock against that exact class — a refactor that regresses any invariant fails CI. Layer B catches real Codex-side drift (renamed/dropped hook events) when a binary is present.
|
|
12
|
+
|
|
13
|
+
## Scope / blast radius
|
|
14
|
+
- `scope-coherence-checkpoint.js` already ships to all agents via always-overwrite migration (`PostUpdateMigrator` line ~1698) and `installCodexHooks` is called from `migrateHooks` gated on codex-cli (line ~1655) — so existing Codex agents pick up the new Stop wiring on update (migration parity satisfied, no new migration needed). `validateHookReferences` guards against a dangling reference.
|
|
15
|
+
- The canary is invoked only from its unit test (the established pattern for the existing Codex canaries — CI drift lock), so it adds zero runtime cost on the hot path.
|
|
16
|
+
|
|
17
|
+
## Signal vs Authority
|
|
18
|
+
- Unchanged. scope-coherence is a low-context Stop-trigger that routes to the server; it never holds blocking authority of its own beyond the existing grounding-pause. The canary is pure verification — no authority, no runtime gating.
|
|
19
|
+
|
|
20
|
+
## Over-block / autonomy risk
|
|
21
|
+
- scope-coherence defaults to `approve` and self-throttles (depth threshold + 30-min cooldown), so it cannot loop an autonomous Codex run. The deferral-detector already runs on Codex Stop the same way (proven-compatible precedent).
|
|
22
|
+
|
|
23
|
+
## Honesty note — PostCompact NOT shipped
|
|
24
|
+
- A WIP that wired `compaction-recovery.sh` to Codex's `PostCompact` event was set aside this session after verifying (against the 0.133 binary schema) that `PostCompact` exposes only `continue/stopReason/suppressOutput/systemMessage` — no `additionalContext`, the only field that re-injects context. Only `SessionStart`/`UserPromptSubmit` carry it, and Codex's `SessionStart` triggers are `startup/resume/clear` (no `compact`). So that wiring would have installed a hook that cannot re-inject identity — dead on arrival. Compaction-recovery parity on Codex needs a different mechanism; tracked, not shipped.
|
|
25
|
+
|
|
26
|
+
## Rollback
|
|
27
|
+
- Revert the one-line `Stop` array edit + delete the canary module + test. No data migration, no config change.
|
|
28
|
+
|
|
29
|
+
## Tests
|
|
30
|
+
- `installCodexHooks.test.ts`: +1 test asserting the full Stop review trio (response-review + deferral + scope-coherence). 8 green.
|
|
31
|
+
- `codexHookContractCanary.test.ts`: 6 tests — layer-A invariants always asserted; layer-B skip-not-fail when no binary; binary-probed branch asserts all required events. Green.
|
|
32
|
+
|
|
33
|
+
## Publish
|
|
34
|
+
- Feature branch `echo/codex-parity-audit`. Targets a patch release on merge.
|