kijito-tools 0.2.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.
Files changed (104) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +6 -0
  3. package/README.md +220 -0
  4. package/bin/cli.js +34 -0
  5. package/install.sh +161 -0
  6. package/package.json +50 -0
  7. package/providers/_shared/wake-core.mjs +358 -0
  8. package/providers/claude/CLAUDE.md.snippet +31 -0
  9. package/providers/claude/install.sh +186 -0
  10. package/providers/claude/scripts/arm-session.sh +66 -0
  11. package/providers/claude/scripts/claude-armed.sh +101 -0
  12. package/providers/claude/scripts/heartbeat-watchdog.sh +209 -0
  13. package/providers/claude/scripts/inbox-selftest.sh +277 -0
  14. package/providers/claude/scripts/kijito-persona-lib.sh +94 -0
  15. package/providers/claude/scripts/kijito-qa-pass.sh +12 -0
  16. package/providers/claude/scripts/lifecycle-lib.sh +167 -0
  17. package/providers/claude/scripts/myctx.sh +46 -0
  18. package/providers/claude/scripts/self-clear.sh +134 -0
  19. package/providers/claude/scripts/session-autosend.sh +85 -0
  20. package/providers/claude/scripts/session-catchup-hint.sh +330 -0
  21. package/providers/claude/scripts/statusline-context.sh +67 -0
  22. package/providers/claude/skills/kijito-qa-memory/SKILL.md +110 -0
  23. package/providers/claude/skills/kijito-recall/SKILL.md +53 -0
  24. package/providers/claude/skills/kijito-start/SKILL.md +122 -0
  25. package/providers/claude/wiring/README.md +93 -0
  26. package/providers/claude/wiring/kijito-heartbeat@.service +21 -0
  27. package/providers/codex/README.md +75 -0
  28. package/providers/codex/codex-kijito-parity-plan.md +265 -0
  29. package/providers/codex/docs-codex-setup.md +64 -0
  30. package/providers/codex/install.mjs +184 -0
  31. package/providers/codex/n0-capability-probe-protocol.md +472 -0
  32. package/providers/codex/n0-harness/README.md +57 -0
  33. package/providers/codex/n0-harness/cli.mjs +58 -0
  34. package/providers/codex/n0-harness/evidence-manifest.mjs +77 -0
  35. package/providers/codex/n0-harness/fixture.mjs +279 -0
  36. package/providers/codex/n0-harness/lib.mjs +141 -0
  37. package/providers/codex/n0-harness/manifest.mjs +36 -0
  38. package/providers/codex/n0-harness/oracle.mjs +464 -0
  39. package/providers/codex/n0-harness/parser.mjs +200 -0
  40. package/providers/codex/n0-harness/prompt.mjs +43 -0
  41. package/providers/codex/n0-harness/snapshot.mjs +120 -0
  42. package/providers/codex/n0-harness/specimen.mjs +37 -0
  43. package/providers/codex/notify/kijito-notify-count.mjs +117 -0
  44. package/providers/codex/notify/kijito-notify-count.test.mjs +76 -0
  45. package/providers/codex/plans/gate4-battery-log.md +184 -0
  46. package/providers/codex/plans/gate4-battery-protocol.md +43 -0
  47. package/providers/codex/plans/hive-user-first-plan-QA.md +34 -0
  48. package/providers/codex/plans/hive-user-first-plan.md +247 -0
  49. package/providers/codex/release-manifest.json +53 -0
  50. package/providers/codex/same-chat-continuation-plan-gate.md +235 -0
  51. package/providers/codex/same-chat-continuation-plan.md +458 -0
  52. package/providers/codex/skills/kijito-qa-memory/SKILL.md +193 -0
  53. package/providers/codex/skills/kijito-qa-memory/agents/openai.yaml +4 -0
  54. package/providers/codex/skills/kijito-recall/SKILL.md +53 -0
  55. package/providers/codex/skills/kijito-start/SKILL.md +173 -0
  56. package/providers/codex/skills/kijito-start/agents/openai.yaml +4 -0
  57. package/providers/codex/test/n0-cli-refusal.test.mjs +31 -0
  58. package/providers/codex/test/n0-counterexample-matrix.test.mjs +118 -0
  59. package/providers/codex/test/n0-guard-census-core.mjs +1372 -0
  60. package/providers/codex/test/n0-guard-census.json +35479 -0
  61. package/providers/codex/test/n0-guard-census.mjs +31 -0
  62. package/providers/codex/test/n0-guard-census.test.mjs +374 -0
  63. package/providers/codex/test/n0-guard-counterexamples.mjs +1068 -0
  64. package/providers/codex/test/n0-guard-independent-projection.mjs +70 -0
  65. package/providers/codex/test/n0-guard-independent-projection.test.mjs +53 -0
  66. package/providers/codex/test/n0-guard-manifest-author.mjs +424 -0
  67. package/providers/codex/test/n0-guard-mutation-operators.test.mjs +244 -0
  68. package/providers/codex/test/n0-guard-mutation-runner.mjs +599 -0
  69. package/providers/codex/test/n0-guard-remainder-classes.test.mjs +314 -0
  70. package/providers/codex/test/n0-guard-sanitizer-pairs.test.mjs +68 -0
  71. package/providers/codex/test/n0-guard-shared-context-predicates.test.mjs +84 -0
  72. package/providers/codex/test/n0-harness.test.mjs +553 -0
  73. package/providers/codex/test/n0-notable-regressions.test.mjs +70 -0
  74. package/providers/codex/test/n0-subject-boundary.mjs +97 -0
  75. package/providers/codex/test/n0-subject-boundary.test.mjs +72 -0
  76. package/providers/codex/test/same-chat-plan-preflight.sh +167 -0
  77. package/providers/codex/tools/refresh-manifest.mjs +81 -0
  78. package/providers/codex/wake-helper/TRANSPORT-NOTES.md +25 -0
  79. package/providers/codex/wake-helper/integration.test.mjs +285 -0
  80. package/providers/codex/wake-helper/kijito-wake-helper.mjs +488 -0
  81. package/providers/codex/wake-helper/kijito-wake-helper.test.mjs +128 -0
  82. package/providers/codex/wake-helper/mock-daemon.mjs +64 -0
  83. package/providers/codex/wake-helper/status-probe.mjs +45 -0
  84. package/providers/codex/wake-helper/ws-uds.mjs +153 -0
  85. package/providers/monitor/.github/workflows/publish-npm.yml +38 -0
  86. package/providers/monitor/.github/workflows/publish-pypi.yml +30 -0
  87. package/providers/monitor/CHANGELOG.md +465 -0
  88. package/providers/monitor/IMPORT-PROVENANCE.md +68 -0
  89. package/providers/monitor/LICENSE +202 -0
  90. package/providers/monitor/NOTICE +7 -0
  91. package/providers/monitor/OPAQUE-OUTPUT-ENFORCEMENT.md +45 -0
  92. package/providers/monitor/README.md +571 -0
  93. package/providers/monitor/RELEASING.md +125 -0
  94. package/providers/monitor/arm-hive-monitor.sh +13 -0
  95. package/providers/monitor/bin/cli.js +43 -0
  96. package/providers/monitor/com.kijito.inbox-monitor.plist.template +59 -0
  97. package/providers/monitor/docs/DESIGN.md +984 -0
  98. package/providers/monitor/kijito-inbox-monitor@.service.template +95 -0
  99. package/providers/monitor/kijito_inbox_monitor.py +3666 -0
  100. package/providers/monitor/package.json +35 -0
  101. package/providers/monitor/pyproject.toml +42 -0
  102. package/providers/monitor/scripts/mutation-check.py +399 -0
  103. package/providers/monitor/scripts/prepublish-gate.sh +284 -0
  104. package/providers/monitor/test_kijito_monitor.py +5609 -0
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import { createHash } from "node:crypto";
3
+ import { readFileSync, readdirSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
8
+ const SUBJECT_DIR = "providers/codex/n0-harness";
9
+ const BASE_COMMIT = "afe5afef156e45523d129525360dfff05a11045c";
10
+
11
+ const BASE_HASHES = new Map(Object.entries({
12
+ "providers/codex/n0-harness/cli.mjs": "0662c199ce81ac8adb315293db59903d2dab16a61c0a51a0985ce722a9013cef",
13
+ "providers/codex/n0-harness/evidence-manifest.mjs": "7d1f9f4a578e532f9e80f9c045f1f9508258c7d68258aa5146fced30f5c70008",
14
+ "providers/codex/n0-harness/fixture.mjs": "ce9f8f709e9673b074e6a9f2322fa6d1718a7551c37ecb7af2966466d11c2725",
15
+ "providers/codex/n0-harness/lib.mjs": "f32cb7f7347f11c2a4ec61194919bca30cb535f167a4e08f0ad6ac1ac276c8bf",
16
+ "providers/codex/n0-harness/manifest.mjs": "4d9531d40efa9bb7455a121630195114cadb0517a0f525c3d0a009131bb7b55a",
17
+ "providers/codex/n0-harness/oracle.mjs": "4ecd55183def821b2f67ec9517d4aa4c042450a852d404a3abfdb9537ff42a5e",
18
+ "providers/codex/n0-harness/parser.mjs": "4844fadf2fc8d62421dff9d7474e837d48d1ab13da9947ee1559d7063c768146",
19
+ "providers/codex/n0-harness/prompt.mjs": "1a84084a9f061656c6059c15ff53c508ca5e14fd3831849b90213705bb31c8bb",
20
+ "providers/codex/n0-harness/snapshot.mjs": "1067a4ebe71e027fc1dc6aa5df397efd2abb30f1a52723f12ba2f62813996cbd",
21
+ "providers/codex/n0-harness/specimen.mjs": "6adace1ba6ab571624533aa72c4f0c7dfe474b6a797ad247488c62e0dd23dc25",
22
+ "providers/codex/test/n0-harness.test.mjs": "fb0778e2f1d0ae95dab1cf19f13cbac25bc9786ea63167cdf3707d3c0fd04bce",
23
+ }));
24
+
25
+ const CLI_CURRENT_HASH = "f5e20e51391985124ff2ddd53e100e5461ca517e0253480adb3e413be1979001";
26
+ const CLI_REVERSE_PATCHES = [
27
+ [
28
+ ` if (!key?.startsWith("--") || value === undefined) {\n usage();\n return null;\n }`,
29
+ ` if (!key?.startsWith("--") || value === undefined) usage();`,
30
+ ],
31
+ [
32
+ `function main(argv) {\n const parsed = args(argv);\n if (!parsed) return;\n const { command, options } = parsed;`,
33
+ `try {\n const { command, options } = args(process.argv.slice(2));`,
34
+ ],
35
+ [
36
+ ` if (!options.root || !path.isAbsolute(options.root)) {\n usage();\n return;\n }`,
37
+ ` if (!options.root || !path.isAbsolute(options.root)) usage();`,
38
+ ],
39
+ [
40
+ ` if (!options.specimen || !options.evidence) {\n usage();\n return;\n }`,
41
+ ` if (!options.specimen || !options.evidence) usage();`,
42
+ ],
43
+ [
44
+ ` } else {\n usage();\n }\n}\n\ntry {\n main(process.argv.slice(2));\n} catch (error) {`,
45
+ ` } else {\n usage();\n }\n} catch (error) {`,
46
+ ],
47
+ ];
48
+
49
+ function fail(code, detail) {
50
+ throw new Error(`${code}: ${detail}`);
51
+ }
52
+
53
+ function sha256(value) {
54
+ return createHash("sha256").update(value).digest("hex");
55
+ }
56
+
57
+ function replaceExactlyOnce(source, from, to, index) {
58
+ const first = source.indexOf(from);
59
+ if (first === -1 || source.indexOf(from, first + 1) !== -1) {
60
+ fail("N0_SUBJECT_CLI_PATCH", `reverse patch ${index + 1} must match exactly once`);
61
+ }
62
+ return `${source.slice(0, first)}${to}${source.slice(first + from.length)}`;
63
+ }
64
+
65
+ const expectedProduction = [...BASE_HASHES.keys()]
66
+ .filter((file) => file.startsWith(`${SUBJECT_DIR}/`))
67
+ .sort();
68
+ const actualProduction = readdirSync(path.join(ROOT, SUBJECT_DIR), { withFileTypes: true })
69
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".mjs"))
70
+ .map((entry) => `${SUBJECT_DIR}/${entry.name}`)
71
+ .sort();
72
+
73
+ if (JSON.stringify(actualProduction) !== JSON.stringify(expectedProduction)) {
74
+ fail("N0_SUBJECT_INVENTORY", `expected=${JSON.stringify(expectedProduction)} actual=${JSON.stringify(actualProduction)}`);
75
+ }
76
+
77
+ for (const [file, expectedBaseHash] of BASE_HASHES) {
78
+ const source = readFileSync(path.join(ROOT, file));
79
+ const currentHash = sha256(source);
80
+ if (file.endsWith("/cli.mjs")) {
81
+ if (currentHash !== CLI_CURRENT_HASH) {
82
+ fail("N0_SUBJECT_CLI_CURRENT", `expected=${CLI_CURRENT_HASH} actual=${currentHash}`);
83
+ }
84
+ let reconstructedBase = source.toString("utf8");
85
+ for (const [index, [from, to]] of CLI_REVERSE_PATCHES.entries()) {
86
+ reconstructedBase = replaceExactlyOnce(reconstructedBase, from, to, index);
87
+ }
88
+ const reconstructedHash = sha256(reconstructedBase);
89
+ if (reconstructedHash !== expectedBaseHash) {
90
+ fail("N0_SUBJECT_CLI_BASE", `base=${BASE_COMMIT} expected=${expectedBaseHash} actual=${reconstructedHash}`);
91
+ }
92
+ } else if (currentHash !== expectedBaseHash) {
93
+ fail("N0_SUBJECT_DRIFT", `${file} base=${BASE_COMMIT} expected=${expectedBaseHash} actual=${currentHash}`);
94
+ }
95
+ }
96
+
97
+ process.stdout.write(`N0_SUBJECT_BOUNDARY_GREEN base=${BASE_COMMIT} productionFiles=${actualProduction.length} allowedDelta=cli-control-flow\n`);
@@ -0,0 +1,72 @@
1
+ import assert from "node:assert/strict";
2
+ import { afterEach, test } from "node:test";
3
+ import { appendFileSync, cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import path from "node:path";
6
+ import { spawnSync } from "node:child_process";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
10
+ const temporaryRoots = [];
11
+
12
+ afterEach(() => {
13
+ for (const root of temporaryRoots.splice(0)) rmSync(root, { recursive: true, force: true });
14
+ });
15
+
16
+ function makeSpecimen() {
17
+ const root = mkdtempSync(path.join(tmpdir(), "n0-subject-boundary-"));
18
+ temporaryRoots.push(root);
19
+ mkdirSync(path.join(root, "providers/codex/test"), { recursive: true });
20
+ cpSync(path.join(ROOT, "providers/codex/n0-harness"), path.join(root, "providers/codex/n0-harness"), { recursive: true });
21
+ cpSync(path.join(ROOT, "providers/codex/test/n0-subject-boundary.mjs"), path.join(root, "providers/codex/test/n0-subject-boundary.mjs"));
22
+ cpSync(path.join(ROOT, "providers/codex/test/n0-harness.test.mjs"), path.join(root, "providers/codex/test/n0-harness.test.mjs"));
23
+ return root;
24
+ }
25
+
26
+ function run(root) {
27
+ return spawnSync(process.execPath, ["providers/codex/test/n0-subject-boundary.mjs"], {
28
+ cwd: root,
29
+ encoding: "utf8",
30
+ });
31
+ }
32
+
33
+ test("subject boundary accepts only the reviewed current bytes", () => {
34
+ const result = run(makeSpecimen());
35
+ assert.equal(result.status, 0, result.stderr);
36
+ assert.match(result.stdout, /^N0_SUBJECT_BOUNDARY_GREEN /m);
37
+ });
38
+
39
+ test("subject boundary rejects production drift and new modules", () => {
40
+ const driftRoot = makeSpecimen();
41
+ appendFileSync(path.join(driftRoot, "providers/codex/n0-harness/oracle.mjs"), "\n// drift\n");
42
+ const drift = run(driftRoot);
43
+ assert.notEqual(drift.status, 0);
44
+ assert.match(drift.stderr, /N0_SUBJECT_DRIFT/);
45
+
46
+ const inventoryRoot = makeSpecimen();
47
+ writeFileSync(path.join(inventoryRoot, "providers/codex/n0-harness/extra.mjs"), "export {};\n");
48
+ const inventory = run(inventoryRoot);
49
+ assert.notEqual(inventory.status, 0);
50
+ assert.match(inventory.stderr, /N0_SUBJECT_INVENTORY/);
51
+ });
52
+
53
+ test("subject boundary rejects a changed or merely base-restored CLI", () => {
54
+ const changedRoot = makeSpecimen();
55
+ appendFileSync(path.join(changedRoot, "providers/codex/n0-harness/cli.mjs"), "\n// drift\n");
56
+ const changed = run(changedRoot);
57
+ assert.notEqual(changed.status, 0);
58
+ assert.match(changed.stderr, /N0_SUBJECT_CLI_CURRENT/);
59
+
60
+ const baseRoot = makeSpecimen();
61
+ const cli = path.join(baseRoot, "providers/codex/n0-harness/cli.mjs");
62
+ const source = readFileSync(cli, "utf8")
63
+ .replace(` if (!key?.startsWith("--") || value === undefined) {\n usage();\n return null;\n }`, ` if (!key?.startsWith("--") || value === undefined) usage();`)
64
+ .replace(`function main(argv) {\n const parsed = args(argv);\n if (!parsed) return;\n const { command, options } = parsed;`, `try {\n const { command, options } = args(process.argv.slice(2));`)
65
+ .replace(` if (!options.root || !path.isAbsolute(options.root)) {\n usage();\n return;\n }`, ` if (!options.root || !path.isAbsolute(options.root)) usage();`)
66
+ .replace(` if (!options.specimen || !options.evidence) {\n usage();\n return;\n }`, ` if (!options.specimen || !options.evidence) usage();`)
67
+ .replace(` } else {\n usage();\n }\n}\n\ntry {\n main(process.argv.slice(2));\n} catch (error) {`, ` } else {\n usage();\n }\n} catch (error) {`);
68
+ writeFileSync(cli, source);
69
+ const base = run(baseRoot);
70
+ assert.notEqual(base.status, 0);
71
+ assert.match(base.stderr, /N0_SUBJECT_CLI_CURRENT/);
72
+ });
@@ -0,0 +1,167 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
5
+ repo=$(CDPATH= cd -- "$root/../.." && pwd)
6
+ plan="$root/same-chat-continuation-plan.md"
7
+ gate="$root/same-chat-continuation-plan-gate.md"
8
+ readme="$root/README.md"
9
+ repo_readme="$repo/README.md"
10
+ installer="$root/install.mjs"
11
+ parity_plan="$root/codex-kijito-parity-plan.md"
12
+ dispatcher="$repo/install.sh"
13
+
14
+ must_contain() (
15
+ file=$1
16
+ needle=$2
17
+ marker=$3
18
+ if command -v rg >/dev/null 2>&1; then
19
+ rg -F -q -- "$needle" "$file"
20
+ else
21
+ grep -F -q -- "$needle" "$file"
22
+ fi || {
23
+ printf 'RED %s missing: %s\n' "$marker" "$needle" >&2
24
+ exit 1
25
+ }
26
+ printf '%s\n' "$marker"
27
+ )
28
+
29
+ must_not_contain() (
30
+ file=$1
31
+ needle=$2
32
+ marker=$3
33
+ if command -v rg >/dev/null 2>&1; then
34
+ found=$(rg -F -c -- "$needle" "$file" || true)
35
+ else
36
+ found=$(grep -F -c -- "$needle" "$file" || true)
37
+ fi
38
+ found=${found:-0}
39
+ if [ "$found" -ne 0 ]; then
40
+ printf 'RED %s forbidden: %s\n' "$marker" "$needle" >&2
41
+ exit 1
42
+ fi
43
+ )
44
+
45
+ must_count() (
46
+ file=$1
47
+ needle=$2
48
+ expected=$3
49
+ marker=$4
50
+ if command -v rg >/dev/null 2>&1; then
51
+ found=$(rg -F -c -- "$needle" "$file" || true)
52
+ else
53
+ found=$(grep -F -c -- "$needle" "$file" || true)
54
+ fi
55
+ found=${found:-0}
56
+ if [ "$found" -ne "$expected" ]; then
57
+ printf 'RED %s expected %s occurrence(s), found %s: %s\n' \
58
+ "$marker" "$expected" "$found" "$needle" >&2
59
+ exit 1
60
+ fi
61
+ printf '%s\n' "$marker"
62
+ )
63
+
64
+ must_authority_line() (
65
+ line=$1
66
+ marker=$2
67
+ must_count "$plan" "$line" 2 "${marker}_PLAN"
68
+ must_count "$gate" "$line" 1 "${marker}_GATE"
69
+ )
70
+
71
+ # Each marker names a false-pass class in the gate record and is emitted only when the corresponding
72
+ # rejection text is present. This is static presence lint; it does not construct a specimen.
73
+ must_contain "$plan" "If Jason's installed build exposes no such independently" \
74
+ "FP01_IDENTITY_CHANNEL_REJECTED"
75
+ must_contain "$plan" "a receipt without the matching native run identity is RED" \
76
+ "FP02_CHAT_ONLY_WORK_REJECTED"
77
+ must_contain "$plan" "There is no assumed programmatic Scheduled management API" \
78
+ "FP03_UI_API_ASSUMPTION_REJECTED"
79
+ must_contain "$plan" "never retries solely because the lease expired" \
80
+ "FP04_EXPIRED_LEASE_REPLAY_REJECTED"
81
+ must_contain "$plan" '`BLOCKED_ROW(<id>)` and cannot advance the checkpoint' \
82
+ "FP05_POISON_GREEN_REJECTED"
83
+ must_contain "$plan" '`mark_read=true` is courtesy presentation metadata' \
84
+ "FP06_UNREAD_ACK_REJECTED"
85
+ must_contain "$plan" "The versioned, user-authored prompt fixes the allowed tools" \
86
+ "FP07_AUTHORITY_EXPANSION_REJECTED"
87
+ must_contain "$plan" "ARMED is unreachable while PID 38082 or any legacy notifier" \
88
+ "FP08_DOUBLE_CONSUMER_REJECTED"
89
+ must_contain "$plan" "proving the scheduled input queues and never uses steering" \
90
+ "FP09_STEER_REJECTED"
91
+ must_contain "$plan" "Notification, summary," \
92
+ "FP10_SUMMARY_ONLY_REJECTED"
93
+
94
+ for finding in L1 L2 L3 L4 L5 L6 L7 L8 L9 L10; do
95
+ must_contain "$gate" "| $finding " "ROUND1_${finding}_TRACED"
96
+ done
97
+ for finding in LB-1 LB-2 LB-3 LB-4 LB-5 LB-6 LB-7; do
98
+ must_contain "$gate" "| $finding " "ROUND2_${finding}_TRACED"
99
+ done
100
+ for finding in N-1 N-2 N-3; do
101
+ must_contain "$gate" "| $finding " "ROUND3_${finding}_TRACED"
102
+ done
103
+ must_contain "$gate" "| N4-1 " "ROUND5_N4-1_TRACED"
104
+
105
+ # Gate-6 (2026-08-15): the withdrawn dedicated-thread notifier's install path was retired
106
+ # outright with the controller era. The fences these three rows pin are now the RETIREMENT
107
+ # declarations — same intent (nobody reinstalls or re-describes the notifier), stronger state.
108
+ must_contain "$readme" "the controller era is over" "RETIRED_README_FENCE_PRESENT"
109
+ # PR #25 (2026-09-08, public-ready sweep, codex-assessed msg 8877): the root README no longer carries the
110
+ # withdrawal BANNER; the same intent is now the retirement declaration in the provider table. Pin that.
111
+ must_contain "$repo_readme" 'The controller-era runtime is retired' \
112
+ "RETIRED_ROOT_README_FENCE_PRESENT"
113
+ must_contain "$installer" "controller-era full install was retired at gate 6" \
114
+ "RETIRED_INSTALLER_FENCE_PRESENT"
115
+ must_contain "$dispatcher" "controller-era full install retired at gate 6" \
116
+ "RETIRED_DISPATCHER_FENCE_PRESENT"
117
+ must_contain "$parity_plan" "this historical plan does not authorize installation" \
118
+ "WITHDRAWN_PARITY_PLAN_FENCE_PRESENT"
119
+ must_contain "$plan" "PR #5 is closed as withdrawn" "WITHDRAWN_PR_DISPOSITION_PRESENT"
120
+ must_contain "$plan" 'segments, bytes, and start time as `DRAINING_BACKLOG`' \
121
+ "PARTIAL_BACKLOG_DRAIN_PRESENT"
122
+ must_contain "$plan" 'Any release failure enters `CLAIM_RELEASE_FAILED(M)`' \
123
+ "CLAIM_RELEASE_FAILURE_FENCE_PRESENT"
124
+ must_contain "$plan" '`REQUIRES_USER(M)` is a fenced terminal disposition, not a retry state.' \
125
+ "REQUIRES_USER_TERMINAL_PRESENT"
126
+ must_contain "$plan" '`AMBIGUOUS_ACTION(M)` means crash reconciliation cannot prove' \
127
+ "AMBIGUOUS_ACTION_DEFINED"
128
+ must_contain "$plan" 'For every runtime state or production execution bound added by a revision' \
129
+ "FIVE_POINT_STATE_CHECK_PRESENT"
130
+ must_contain "$plan" 'No operator-authored checkpoint or control-plane field may be writable by a scheduled run.' \
131
+ "OPERATOR_WRITE_PARTITION_PRESENT"
132
+ must_contain "$plan" '`CODEX_CONTINUATION_OPERATOR_DECISION_V1` journal' \
133
+ "OPERATOR_JOURNAL_PRESENT"
134
+ must_contain "$plan" 'Exactly one rollout' \
135
+ "TARGET_CHAT_ACQUISITION_PRESENT"
136
+ must_contain "$plan" 'Corrupt derived scan state' \
137
+ "CORRUPT_SCAN_REPAIR_PRESENT"
138
+ must_contain "$plan" 'server age at most 135 seconds' \
139
+ "HEARTBEAT_BOUND_PRESENT"
140
+ must_contain "$plan" 'single inbound row can terminally halt the lane' \
141
+ "TERMINAL_HALT_DISCLOSURE_PRESENT"
142
+ must_contain "$gate" '| `DRAINING_BACKLOG` | enumerated | blocks ARMED |' \
143
+ "STATE_DRAINING_FIVE_POINT_TRACED"
144
+ must_contain "$gate" '| `BLOCKED_ROW(id)` | enumerated | blocks ARMED |' \
145
+ "STATE_BLOCKED_ROW_FIVE_POINT_TRACED"
146
+ must_contain "$gate" '| ten-slice/ten-minute bound → `REQUIRES_USER(id)` | enumerated | blocks ARMED |' \
147
+ "STATE_REQUIRES_USER_FIVE_POINT_TRACED"
148
+ must_contain "$gate" '| `AMBIGUOUS_ACTION(id)` | enumerated | blocks ARMED |' \
149
+ "STATE_AMBIGUOUS_ACTION_FIVE_POINT_TRACED"
150
+ must_contain "$gate" '| `CLAIM_RELEASE_FAILED(id)` | enumerated | blocks ARMED |' \
151
+ "STATE_CLAIM_RELEASE_FAILED_FIVE_POINT_TRACED"
152
+ must_authority_line "**AUTHORITY:** Two consecutive Assay-CLEAN reviews of this exact plan digest open N0a/N0b only." \
153
+ "CANONICAL_AUTHORITY_LINE1"
154
+ must_authority_line "GREEN N0 then authorizes disposable test-persona probes of the current N1-N3 surfaces, but no" \
155
+ "CANONICAL_AUTHORITY_LINE2"
156
+ must_authority_line "Codex-provider or Kijito-server implementation. If N1 rejects the current API, a separate" \
157
+ "CANONICAL_AUTHORITY_LINE3"
158
+ must_authority_line "provider-neutral claim/operator-decision API plan owned by River must receive two consecutive" \
159
+ "CANONICAL_AUTHORITY_LINE4"
160
+ must_authority_line "Assay-CLEAN reviews before any such API code is written. An installable Codex provider remains" \
161
+ "CANONICAL_AUTHORITY_LINE5"
162
+ must_authority_line "forbidden until N0-N3 are GREEN and Jason explicitly accepts N3." \
163
+ "CANONICAL_AUTHORITY_LINE6"
164
+ must_not_contain "$gate" "INTERNAL GOLDEN" "NO_SELF_ATTESTED_GOLDEN"
165
+ must_not_contain "$plan" "Only Assay CLEAN opens N0" "NO_SINGLE_CLEAN_GATE"
166
+
167
+ printf 'PLAN_AUTHOR_PREFLIGHT_GREEN\n'
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ // Recompute the gated hashes in providers/codex/release-manifest.json from the files on disk.
3
+ //
4
+ // WHY THIS IS A SCRIPT AND NOT A HAND EDIT. release-manifest.json records the sha256 of every
5
+ // live executable artifact, and install.mjs (the verify path) refuses to pass when a hash
6
+ // disagrees. That gate is the point — but it means ANY edit to a gated file leaves the manifest
7
+ // stale, and the symptom is a verify-time error that reads like corruption rather than "you
8
+ // forgot to refresh". Regenerating by hand also invites the failure mode where you refresh, then
9
+ // make one more edit, and ship the stale hash.
10
+ //
11
+ // node providers/codex/tools/refresh-manifest.mjs # rewrite the manifest
12
+ // node providers/codex/tools/refresh-manifest.mjs --check # verify only; non-zero if stale
13
+ //
14
+ // --check is what the conformance test runs, so a stale manifest fails the suite instead of
15
+ // surfacing later as a mysterious verify failure.
16
+ //
17
+ // GATE-6 (2026-08-15): the controller-era entries were retired to the manifest's
18
+ // `legacyArtifacts` block (files under legacy/codex-controller-era-2026-08/); this map now covers
19
+ // only the LIVE set, and it deliberately does NOT touch legacyArtifacts — those hashes are frozen
20
+ // provenance of what was retired, and re-stamping them would erase the record.
21
+ //
22
+ // The plan hash is likewise NOT recomputed: it is RECORDED provenance, not a gate (the fold
23
+ // demoted it), and silently re-stamping it would erase the record of which plan revision the
24
+ // release was actually reviewed against.
25
+
26
+ import { createHash } from "node:crypto";
27
+ import fs from "node:fs";
28
+ import path from "node:path";
29
+ import { fileURLToPath } from "node:url";
30
+
31
+ const providerRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
32
+ const manifestFile = path.join(providerRoot, "release-manifest.json");
33
+
34
+ const GATED = {
35
+ // The shared wake core: executable code the wake helper imports at runtime (parseEventLine and
36
+ // fixedWakeText — the event validator and the injection fence). Gated since the fold; stays
37
+ // gated because splitting a gated import chain into gated + ungated halves would leave the
38
+ // fence editable while verify still reports GREEN.
39
+ wakeCoreSha256: path.join(providerRoot, "..", "_shared", "wake-core.mjs"),
40
+ // The CI workflow is the thing that RUNS all of the above. It was a modified companion of every
41
+ // recent round and declared nowhere, so a change to what CI executes left no trace in the
42
+ // manifest that records what the release is.
43
+ workflowSha256: path.join(providerRoot, "..", "..", ".github", "workflows", "test.yml"),
44
+ // Gate-5 native live wake (argus's PR#19 gating ruling, stated as a property: every executable
45
+ // artifact a skill/install path causes a user session to run belongs in the manifest).
46
+ // kijito-start's arm step names the helper, so the helper and its runtime import are gated;
47
+ // the tests + mock join per the gated-tests precedent (they are the helper's safety argument).
48
+ // status-probe.mjs and TRANSPORT-NOTES.md stay ungated: instrument + doc, not on the user path.
49
+ wakeHelperSha256: path.join(providerRoot, "wake-helper", "kijito-wake-helper.mjs"),
50
+ wsUdsSha256: path.join(providerRoot, "wake-helper", "ws-uds.mjs"),
51
+ wakeHelperTestsSha256: path.join(providerRoot, "wake-helper", "kijito-wake-helper.test.mjs"),
52
+ wakeHelperIntegrationTestsSha256: path.join(providerRoot, "wake-helper", "integration.test.mjs"),
53
+ wakeHelperMockDaemonSha256: path.join(providerRoot, "wake-helper", "mock-daemon.mjs"),
54
+ };
55
+
56
+ const sha256 = (file) => createHash("sha256").update(fs.readFileSync(file)).digest("hex");
57
+
58
+ const manifest = JSON.parse(fs.readFileSync(manifestFile, "utf8"));
59
+ const drift = [];
60
+ for (const [key, file] of Object.entries(GATED)) {
61
+ const actual = sha256(file);
62
+ if (manifest.artifacts[key] !== actual) {
63
+ drift.push({ key, file: path.relative(providerRoot, file), was: manifest.artifacts[key], now: actual });
64
+ manifest.artifacts[key] = actual;
65
+ }
66
+ }
67
+
68
+ if (process.argv.includes("--check")) {
69
+ if (drift.length === 0) {
70
+ process.stdout.write("release-manifest.json: all gated hashes current\n");
71
+ } else {
72
+ for (const d of drift) process.stderr.write(`STALE ${d.key} (${d.file})\n manifest: ${d.was}\n on disk: ${d.now}\n`);
73
+ process.stderr.write("Run: node providers/codex/tools/refresh-manifest.mjs\n");
74
+ process.exitCode = 1;
75
+ }
76
+ } else if (drift.length === 0) {
77
+ process.stdout.write("release-manifest.json: already current, nothing written\n");
78
+ } else {
79
+ fs.writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`);
80
+ for (const d of drift) process.stdout.write(`refreshed ${d.key} (${d.file}) -> ${d.now}\n`);
81
+ }
@@ -0,0 +1,25 @@
1
+ # Daemon transport — measured facts (build verification, 2026-08-15 00:3x-00:5xZ)
2
+
3
+ Source: openai/codex tag rust-v0.147.0 (sparse clone, /tmp/codex-src on the Mac) + live smoke
4
+ against a real daemon in a THROWAWAY CODEX_HOME (/tmp/kijito-smoke-*; the staged gate-4
5
+ sandbox untouched).
6
+
7
+ 1. **The control socket is a WebSocket server over the UDS** (`app-server-transport/src/
8
+ transport/unix_socket.rs`: tungstenite `accept_async` on the UnixStream). Raw JSONL
9
+ written to the socket is silently ignored at the handshake layer — measured: initialize
10
+ timed out over both raw socket and `codex app-server proxy` (which proxies bytes, not
11
+ framing).
12
+ 2. **Client dial**: UnixStream connect → RFC6455 client handshake with URL `ws://localhost/rpc`
13
+ (`app-server-client/src/remote.rs`, `UDS_WEBSOCKET_HANDSHAKE_URL`) → JSON-RPC, one message
14
+ per text frame. No auth on UDS (0600 socket mode is the auth; bearer/JWT applies to
15
+ non-loopback websocket listeners only).
16
+ 3. **Daemon needs the managed standalone install** under `$CODEX_HOME/packages/standalone/
17
+ current/codex` — a throwaway home works by symlinking the real `~/.codex/packages`.
18
+ 4. **Live smoke results** (real daemon, 0.147.0): initialize OK (server echoes a composed
19
+ userAgent), `thread/start {cwd, ephemeral:false}` OK (id + status=idle returned),
20
+ `thread/resume {threadId}` on a FRESH thread fails `-32600 no rollout found` — a rollout
21
+ exists only once a thread has recorded turns. The helper's real target (the user's live
22
+ session thread) always has turns; the BATTERY must create its session thread with one real
23
+ turn before attaching the helper (added to battery preconditions).
24
+ 5. Helper + mock + tests all speak the measured transport (`ws-uds.mjs`, zero-dep RFC6455
25
+ client+server-accept; 16/16 tests green on it).