instar 1.2.77 → 1.2.78

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.
@@ -63,4 +63,24 @@ export declare function resolveCodexBinaryForCanary(): string | null;
63
63
  * probes a codex binary if one resolves; otherwise that layer is skipped.
64
64
  */
65
65
  export declare function runCodexHookContractCanary(): CodexHookContractCanaryResult;
66
+ /**
67
+ * Layer C — installed-config drift check (C4). Unlike Layer A (which asserts the BUILDER
68
+ * output) this reads what is ACTUALLY on disk for a specific agent and confirms the gates are
69
+ * present AND trusted: the project `.codex/hooks.json` carries instar's Stop review trio
70
+ * (response-review + claim-intercept-response + scope-coherence) AND every instar hook slot has
71
+ * a `trusted_hash` (and isn't `enabled = false`) in `$CODEX_HOME/config.toml [hooks.state]`.
72
+ * Catches reality drifting from the blueprint — a hand-edited/clobbered hooks.json, an untrusted
73
+ * (dark) agent, or a user-disabled guard — which Layer A cannot see. Runtime/health use
74
+ * (per-agent), not a CI invariant: returns 'skip' when no hooks.json exists.
75
+ */
76
+ export interface InstalledCodexHookTrustCheck {
77
+ status: 'ok' | 'drift' | 'skip';
78
+ hooksJsonPresent: boolean;
79
+ stopTrioInstalled: boolean;
80
+ allArmed: boolean;
81
+ untrusted: string[];
82
+ disabled: string[];
83
+ issues: string[];
84
+ }
85
+ export declare function checkInstalledCodexHookTrust(projectDir: string, codexHome?: string): InstalledCodexHookTrustCheck;
66
86
  //# sourceMappingURL=codexHookContractCanary.d.ts.map
@@ -1 +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,oBAAoB,EAAE,OAAO,CAAC;QAC9B,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,CAoG1E"}
1
+ {"version":3,"file":"codexHookContractCanary.d.ts","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/canary/codexHookContractCanary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AASH,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,oBAAoB,EAAE,OAAO,CAAC;QAC9B,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,CAoG1E;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,MAAM,CAAC;IAChC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAID,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,4BAA4B,CAqCjH"}
@@ -40,6 +40,7 @@ import path from 'node:path';
40
40
  import os from 'node:os';
41
41
  import { execFileSync } from 'node:child_process';
42
42
  import { buildInstarCodexHookGroups } from '../../../../core/installCodexHooks.js';
43
+ import { codexHooksArmingStatus, expectedHookSlots } from '../../../../core/codexHookTrust.js';
43
44
  /** Hook events instar's enforcement layer registers and depends on. */
44
45
  export const REQUIRED_CODEX_HOOK_EVENTS = [
45
46
  'PreToolUse',
@@ -189,4 +190,49 @@ export function runCodexHookContractCanary() {
189
190
  },
190
191
  };
191
192
  }
193
+ const STOP_TRIO = ['response-review.js', 'claim-intercept-response.js', 'scope-coherence-checkpoint.js'];
194
+ export function checkInstalledCodexHookTrust(projectDir, codexHome) {
195
+ const home = codexHome || path.join(os.homedir(), '.codex');
196
+ const issues = [];
197
+ let realProjectDir = projectDir;
198
+ try {
199
+ realProjectDir = fs.realpathSync(projectDir);
200
+ }
201
+ catch { /* use as-is */ }
202
+ const hooksJsonPath = path.join(realProjectDir, '.codex', 'hooks.json');
203
+ let hooks;
204
+ try {
205
+ hooks = (JSON.parse(fs.readFileSync(hooksJsonPath, 'utf-8')).hooks) ?? {};
206
+ }
207
+ catch {
208
+ return { status: 'skip', hooksJsonPresent: false, stopTrioInstalled: false, allArmed: false, untrusted: [], disabled: [], issues: ['no .codex/hooks.json'] };
209
+ }
210
+ const stopCmds = (hooks.Stop?.[0]?.hooks ?? []).map((h) => h.command ?? '');
211
+ const stopTrioInstalled = STOP_TRIO.every((s) => stopCmds.some((c) => c.includes(s)));
212
+ if (!stopTrioInstalled)
213
+ issues.push(`installed Stop trio incomplete: have [${stopCmds.map((c) => c.split('/').pop()).join(', ')}], want ${STOP_TRIO.join(' + ')}`);
214
+ if (stopCmds.some((c) => c.includes('deferral-detector.js')))
215
+ issues.push('deferral-detector.js is on Stop in the installed config (it belongs on PreToolUse)');
216
+ let configBody = '';
217
+ try {
218
+ configBody = fs.readFileSync(path.join(home, 'config.toml'), 'utf-8');
219
+ }
220
+ catch { /* none = nothing trusted */ }
221
+ const slots = expectedHookSlots(hooks);
222
+ const status = codexHooksArmingStatus(configBody, hooksJsonPath, slots);
223
+ if (status.untrusted.length)
224
+ issues.push(`untrusted (dark) slots: ${status.untrusted.join(', ')}`);
225
+ if (status.disabled.length)
226
+ issues.push(`explicitly disabled slots: ${status.disabled.join(', ')}`);
227
+ const ok = stopTrioInstalled && status.allArmed && !stopCmds.some((c) => c.includes('deferral-detector.js'));
228
+ return {
229
+ status: ok ? 'ok' : 'drift',
230
+ hooksJsonPresent: true,
231
+ stopTrioInstalled,
232
+ allArmed: status.allArmed,
233
+ untrusted: status.untrusted,
234
+ disabled: status.disabled,
235
+ issues,
236
+ };
237
+ }
192
238
  //# sourceMappingURL=codexHookContractCanary.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"codexHookContractCanary.js","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/canary/codexHookContractCanary.ts"],"names":[],"mappings":"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"}
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,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.2.77",
3
+ "version": "1.2.78",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+ // safe-git-allow: pre-push bootstrap — read-only git only (merge-base / rev-parse
3
+ // / diff --name-only); runs before TS is compiled, so it can't use SafeGitExecutor.
4
+ /**
5
+ * pre-push-e2e-scope — run the END-TO-END suite for the area being pushed.
6
+ *
7
+ * Why: the pre-push smoke tier (`test:push` / `test:smoke`) uses
8
+ * `vitest.push.config.ts`, which EXCLUDES `tests/e2e/**`. So a change can pass
9
+ * pre-push + the watched CI checks yet break the separate `E2E Tests` CI job —
10
+ * exactly what turned main red in PR #381 (the threadline single-store refactor
11
+ * left two e2e test harnesses stale; fixed in #383). A red e2e job blocks the
12
+ * whole merge queue.
13
+ *
14
+ * This gate closes that hole structurally: when the push touches an
15
+ * "e2e-load-bearing" source area, it runs that area's e2e suite BEFORE the push
16
+ * is allowed — automatically, not by memory. It is PATH-SCOPED, so unrelated
17
+ * pushes pay nothing.
18
+ *
19
+ * Opt out: INSTAR_PRE_PUSH_E2E_SKIP=1 git push (CI still runs the full e2e job)
20
+ */
21
+
22
+ import { execSync, spawnSync } from 'node:child_process';
23
+
24
+ if (process.env.INSTAR_PRE_PUSH_E2E_SKIP === '1' || process.env.INSTAR_PRE_PUSH_SKIP === '1') {
25
+ console.log('⏭️ pre-push e2e scope skipped (env) — CI still runs the full e2e job.');
26
+ process.exit(0);
27
+ }
28
+
29
+ /**
30
+ * Map a changed-source-path matcher → the e2e suite path(s) that exercise it.
31
+ * Extend this as more areas gain e2e coverage. Keep entries narrow so the gate
32
+ * stays fast and only fires for the area actually changed.
33
+ */
34
+ const SCOPE_MAP = [
35
+ { match: /^src\/threadline\//, e2e: ['tests/e2e/threadline/'] },
36
+ { match: /^tests\/e2e\/threadline\//, e2e: ['tests/e2e/threadline/'] },
37
+ { match: /^tests\/helpers\/TestThreadResumeMap\.ts$/, e2e: ['tests/e2e/threadline/'] },
38
+ ];
39
+
40
+ let changed = [];
41
+ try {
42
+ // Changes on this branch since it diverged from origin/main. The pre-push
43
+ // hook already fetched origin/main; fall back to HEAD~1 if the ref is absent.
44
+ let base = '';
45
+ try {
46
+ base = execSync('git merge-base origin/main HEAD', { encoding: 'utf-8' }).trim();
47
+ } catch {
48
+ base = execSync('git rev-parse HEAD~1', { encoding: 'utf-8' }).trim();
49
+ }
50
+ const out = execSync(`git diff --name-only ${base} HEAD`, { encoding: 'utf-8' });
51
+ changed = out.split('\n').map(s => s.trim()).filter(Boolean);
52
+ } catch (err) {
53
+ console.warn(`pre-push-e2e-scope: could not compute changed files (${err instanceof Error ? err.message : err}) — skipping (CI still runs e2e).`);
54
+ process.exit(0);
55
+ }
56
+
57
+ const suites = new Set();
58
+ for (const file of changed) {
59
+ for (const rule of SCOPE_MAP) {
60
+ if (rule.match.test(file)) rule.e2e.forEach(s => suites.add(s));
61
+ }
62
+ }
63
+
64
+ if (suites.size === 0) {
65
+ process.exit(0); // nothing e2e-load-bearing changed — fast path
66
+ }
67
+
68
+ const suiteList = [...suites];
69
+ console.log(`🧪 pre-push e2e scope: changed files touch ${suiteList.join(', ')} — running their e2e suite(s)...`);
70
+ console.log(' Opt out: INSTAR_PRE_PUSH_E2E_SKIP=1 git push (CI still runs the full e2e job)');
71
+
72
+ const res = spawnSync(
73
+ 'npx',
74
+ ['vitest', 'run', '--config', 'vitest.e2e.config.ts', ...suiteList],
75
+ { stdio: 'inherit', encoding: 'utf-8' },
76
+ );
77
+
78
+ if (res.status !== 0) {
79
+ console.error('❌ Scoped e2e suite failed. Fix before pushing, or INSTAR_PRE_PUSH_E2E_SKIP=1 to bypass (CI will still catch it).');
80
+ process.exit(1);
81
+ }
82
+ console.log('✅ Scoped e2e suite passed.');
83
+ process.exit(0);
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * safe-merge — merge a PR ONLY after every check (incl. the e2e job) is green.
4
+ *
5
+ * Why: we merge with `gh pr merge --admin` to jump the hot-branch queue, but
6
+ * `--admin` BYPASSES branch-protection's required-checks enforcement — which is
7
+ * how PR #381 merged while the `E2E Tests` job was red and turned main red for
8
+ * everyone. This wrapper re-imposes the requirement that `--admin` removes:
9
+ * it waits for all checks to finish, refuses the merge if ANY check failed (and
10
+ * specifically verifies an e2e check ran + passed), and only then merges.
11
+ *
12
+ * Usage: node scripts/safe-merge.mjs <PR#> [--admin] [--squash|--merge|--rebase]
13
+ * --admin still allowed (for the BEHIND/hot-branch case) — but ONLY
14
+ * reached AFTER this script has confirmed every check is green.
15
+ * default merge method: --merge
16
+ *
17
+ * Exit non-zero (and do NOT merge) on any red/failed check.
18
+ */
19
+
20
+ import { execSync, spawnSync } from 'node:child_process';
21
+
22
+ const REPO = 'JKHeadley/instar';
23
+ const args = process.argv.slice(2);
24
+ const pr = args.find(a => /^\d+$/.test(a));
25
+ const useAdmin = args.includes('--admin');
26
+ const method = args.find(a => ['--squash', '--merge', '--rebase'].includes(a)) || '--merge';
27
+
28
+ if (!pr) {
29
+ console.error('usage: node scripts/safe-merge.mjs <PR#> [--admin] [--squash|--merge|--rebase]');
30
+ process.exit(2);
31
+ }
32
+
33
+ function checks() {
34
+ // gh pr checks exits non-zero when checks are failing/pending; capture either way.
35
+ const r = spawnSync('gh', ['pr', 'checks', pr, '--repo', REPO], { encoding: 'utf-8' });
36
+ return (r.stdout || '') + (r.stderr || '');
37
+ }
38
+
39
+ // Poll until nothing is pending (cap ~20 min).
40
+ console.log(`safe-merge: waiting for PR #${pr} checks to finish...`);
41
+ const deadline = Date.now() + 20 * 60 * 1000;
42
+ let out = checks();
43
+ while (/\bpending\b/.test(out)) {
44
+ if (Date.now() > deadline) {
45
+ console.error('safe-merge: timed out waiting for checks. NOT merging.');
46
+ process.exit(1);
47
+ }
48
+ execSync('sleep 15');
49
+ out = checks();
50
+ }
51
+
52
+ // Parse rows: "<name>\t<state>\t<elapsed>\t<url>". States: pass | fail | skipping | ...
53
+ const rows = out.split('\n').map(l => l.trim()).filter(Boolean);
54
+ const failed = [];
55
+ let sawE2e = false;
56
+ let e2ePassed = false;
57
+ for (const line of rows) {
58
+ const cols = line.split('\t').map(c => c.trim());
59
+ const name = cols[0] || '';
60
+ const state = (cols[1] || '').toLowerCase();
61
+ if (!name || !state) continue;
62
+ const ok = state === 'pass' || state === 'skipping' || state === 'neutral';
63
+ if (/e2e/i.test(name)) {
64
+ sawE2e = true;
65
+ if (state === 'pass') e2ePassed = true;
66
+ }
67
+ if (!ok) failed.push(`${name}: ${state}`);
68
+ }
69
+
70
+ if (failed.length > 0) {
71
+ console.error(`safe-merge: REFUSING — ${failed.length} check(s) not green:\n ${failed.join('\n ')}`);
72
+ process.exit(1);
73
+ }
74
+ if (!sawE2e) {
75
+ console.error('safe-merge: REFUSING — no e2e check found in the PR checks. The e2e job is the one --admin bypasses; do not merge without it. (Override only with explicit human sign-off.)');
76
+ process.exit(1);
77
+ }
78
+ if (!e2ePassed) {
79
+ console.error('safe-merge: REFUSING — an e2e check is present but did not report pass.');
80
+ process.exit(1);
81
+ }
82
+
83
+ console.log(`safe-merge: all checks green (e2e confirmed). Merging PR #${pr} (${method}${useAdmin ? ' --admin' : ''})...`);
84
+ const mergeArgs = ['pr', 'merge', pr, '--repo', REPO, method];
85
+ if (useAdmin) mergeArgs.push('--admin');
86
+ const m = spawnSync('gh', mergeArgs, { stdio: 'inherit', encoding: 'utf-8' });
87
+ process.exit(m.status ?? 0);
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-25T19:25:24.914Z",
5
- "instarVersion": "1.2.77",
4
+ "generatedAt": "2026-05-25T19:49:20.468Z",
5
+ "instarVersion": "1.2.78",
6
6
  "entryCount": 191,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,49 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+ <!-- patch = bug fixes, refactors, test additions, doc updates -->
5
+
6
+ ## What Changed
7
+
8
+ Two Codex-parity follow-ups from the codex-full-parity spec (§7), both hardening the Codex
9
+ safety-hook layer that shipped in the previous release:
10
+
11
+ 1. **Live-config drift detection for the Codex safety guards (C4).** A new check
12
+ (`checkInstalledCodexHookTrust`) reads what's ACTUALLY installed on a Codex agent — its
13
+ `.codex/hooks.json` plus the trust state in `~/.codex/config.toml` — and reports `ok` / `drift`
14
+ / `skip`. It confirms the end-of-turn review trio (response-review + claim-intercept-response +
15
+ scope-coherence) is present AND trusted (not disabled), and that the anti-deferral hook hasn't
16
+ drifted back onto the Stop event. The existing canary asserts the *blueprint* (what instar would
17
+ install); this catches *reality* drifting from it — a hand-edited or clobbered config, a
18
+ never-trusted ("dark") agent, or a guard a user turned off — which the blueprint check can't see.
19
+
20
+ 2. **Stop-payload runtime-verified (B1).** The two Codex Stop review-checkers read a
21
+ `last_assistant_message` field. We had confirmed Codex's binary *declares* that field; this
22
+ release confirms it at RUNTIME — a live Codex 0.133 turn was captured and the field held the
23
+ exact agent reply. So those checkers genuinely receive the response on Codex. No code change;
24
+ this closes the schema-vs-runtime gap the convergence review flagged.
25
+
26
+ ## What to Tell Your User
27
+
28
+ - **Your Codex agent's safety guards now have a reality check**: "There's a new check that looks at
29
+ what's actually wired and switched on for a Codex agent — not just what should be — so a guard
30
+ can't quietly end up uninstalled, untrusted, or turned off without it being noticeable."
31
+ - Nothing for you to do — it ships automatically on update.
32
+
33
+ ## Summary of New Capabilities
34
+
35
+ | Capability | How to Use |
36
+ |-----------|-----------|
37
+ | Codex installed-config drift check (`checkInstalledCodexHookTrust`) | Programmatic — read-only health check of a Codex agent's hooks + trust state |
38
+ | Codex Stop review-checkers runtime-verified | Automatic — no action needed |
39
+
40
+ ## Evidence
41
+
42
+ - **C4**: `checkInstalledCodexHookTrust` reads the on-disk `.codex/hooks.json` + `config.toml`
43
+ `[hooks.state]` (reusing `codexHookTrust`) and returns `ok`/`drift`/`skip`. 5 new unit tests cover
44
+ skip (no hooks.json), drift (untrusted/dark agent), ok (trio present + trusted), drift (a slot
45
+ explicitly disabled), and a clobbered config where `deferral-detector` wrongly sits on Stop. 28
46
+ codex-area tests green; `tsc` clean. Side-effects review: `upgrades/side-effects/codex-parity-c4-canary-drift.md`.
47
+ - **B1**: captured a real Codex 0.133 Stop payload from a live `codex exec` turn; payload keys
48
+ include `last_assistant_message`, which held the exact reply ("The quick brown fox jumps over the
49
+ lazy dog."). Confirms `response-review.js` + `claim-intercept-response.js` are fed at runtime on Codex.
@@ -0,0 +1,33 @@
1
+ # Side-Effects Review: C4 — canary live-config drift detector + B1 runtime-verified
2
+
3
+ ## Change
4
+ 1. **C4** — new `checkInstalledCodexHookTrust(projectDir, codexHome?)` in codexHookContractCanary.ts:
5
+ reads the ACTUAL installed `.codex/hooks.json` + `$CODEX_HOME/config.toml [hooks.state]` (reusing
6
+ codexHookTrust) and reports `ok` / `drift` / `skip` — asserting the Stop review trio is present
7
+ AND every instar slot is trusted (not enabled=false), and that deferral-detector is NOT on Stop.
8
+ Layer A asserts the BUILDER output; Layer C catches reality drifting (clobbered hooks.json,
9
+ dark/untrusted agent, user-disabled guard). Runtime/per-agent (skip when no hooks.json).
10
+ 2. **B1** — spec updated: response-review/claim-intercept Codex Stop-payload is now RUNTIME-VERIFIED
11
+ (captured a real Codex 0.133 Stop payload; `last_assistant_message` held the exact reply). No code.
12
+
13
+ ## Why
14
+ Convergence review §7 C4 + B1. C4 makes the drift-alarm check reality, not just the blueprint —
15
+ the reviewer's point that a hardcoded-trio assertion would encode the next drift as correct. B1
16
+ closes the schema≠runtime gap for the two Stop review-checkers.
17
+
18
+ ## Scope / blast radius
19
+ - C4 is a new read-only function (no mutation); reuses codexHookTrust (pure). Imported at top
20
+ (no lazy require — that broke under the ESM test runner). Not yet wired into a scheduled health
21
+ check — it's a building block a runtime caller (G5 arming canary / health) can use. RULE 3:
22
+ the canary module already carries a Rule 3.1 rationale; this extends it (read-only config parse).
23
+ - B1: docs-only (spec status update).
24
+
25
+ ## Signal vs Authority / Rollback
26
+ - Read-only check, no authority. Rollback: remove the function + tests + revert the spec line.
27
+
28
+ ## Tests
29
+ - codexHookContractCanary.test.ts: +5 (skip/drift-untrusted/ok/disabled/clobbered-trio). 11 green.
30
+ installCodexHooks + codexHookTrust unaffected. tsc clean.
31
+
32
+ ## Publish
33
+ - PR to JKHeadley/main (codex-parity-followups). Squash-merge.