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.
- package/LICENSE +201 -0
- package/NOTICE +6 -0
- package/README.md +220 -0
- package/bin/cli.js +34 -0
- package/install.sh +161 -0
- package/package.json +50 -0
- package/providers/_shared/wake-core.mjs +358 -0
- package/providers/claude/CLAUDE.md.snippet +31 -0
- package/providers/claude/install.sh +186 -0
- package/providers/claude/scripts/arm-session.sh +66 -0
- package/providers/claude/scripts/claude-armed.sh +101 -0
- package/providers/claude/scripts/heartbeat-watchdog.sh +209 -0
- package/providers/claude/scripts/inbox-selftest.sh +277 -0
- package/providers/claude/scripts/kijito-persona-lib.sh +94 -0
- package/providers/claude/scripts/kijito-qa-pass.sh +12 -0
- package/providers/claude/scripts/lifecycle-lib.sh +167 -0
- package/providers/claude/scripts/myctx.sh +46 -0
- package/providers/claude/scripts/self-clear.sh +134 -0
- package/providers/claude/scripts/session-autosend.sh +85 -0
- package/providers/claude/scripts/session-catchup-hint.sh +330 -0
- package/providers/claude/scripts/statusline-context.sh +67 -0
- package/providers/claude/skills/kijito-qa-memory/SKILL.md +110 -0
- package/providers/claude/skills/kijito-recall/SKILL.md +53 -0
- package/providers/claude/skills/kijito-start/SKILL.md +122 -0
- package/providers/claude/wiring/README.md +93 -0
- package/providers/claude/wiring/kijito-heartbeat@.service +21 -0
- package/providers/codex/README.md +75 -0
- package/providers/codex/codex-kijito-parity-plan.md +265 -0
- package/providers/codex/docs-codex-setup.md +64 -0
- package/providers/codex/install.mjs +184 -0
- package/providers/codex/n0-capability-probe-protocol.md +472 -0
- package/providers/codex/n0-harness/README.md +57 -0
- package/providers/codex/n0-harness/cli.mjs +58 -0
- package/providers/codex/n0-harness/evidence-manifest.mjs +77 -0
- package/providers/codex/n0-harness/fixture.mjs +279 -0
- package/providers/codex/n0-harness/lib.mjs +141 -0
- package/providers/codex/n0-harness/manifest.mjs +36 -0
- package/providers/codex/n0-harness/oracle.mjs +464 -0
- package/providers/codex/n0-harness/parser.mjs +200 -0
- package/providers/codex/n0-harness/prompt.mjs +43 -0
- package/providers/codex/n0-harness/snapshot.mjs +120 -0
- package/providers/codex/n0-harness/specimen.mjs +37 -0
- package/providers/codex/notify/kijito-notify-count.mjs +117 -0
- package/providers/codex/notify/kijito-notify-count.test.mjs +76 -0
- package/providers/codex/plans/gate4-battery-log.md +184 -0
- package/providers/codex/plans/gate4-battery-protocol.md +43 -0
- package/providers/codex/plans/hive-user-first-plan-QA.md +34 -0
- package/providers/codex/plans/hive-user-first-plan.md +247 -0
- package/providers/codex/release-manifest.json +53 -0
- package/providers/codex/same-chat-continuation-plan-gate.md +235 -0
- package/providers/codex/same-chat-continuation-plan.md +458 -0
- package/providers/codex/skills/kijito-qa-memory/SKILL.md +193 -0
- package/providers/codex/skills/kijito-qa-memory/agents/openai.yaml +4 -0
- package/providers/codex/skills/kijito-recall/SKILL.md +53 -0
- package/providers/codex/skills/kijito-start/SKILL.md +173 -0
- package/providers/codex/skills/kijito-start/agents/openai.yaml +4 -0
- package/providers/codex/test/n0-cli-refusal.test.mjs +31 -0
- package/providers/codex/test/n0-counterexample-matrix.test.mjs +118 -0
- package/providers/codex/test/n0-guard-census-core.mjs +1372 -0
- package/providers/codex/test/n0-guard-census.json +35479 -0
- package/providers/codex/test/n0-guard-census.mjs +31 -0
- package/providers/codex/test/n0-guard-census.test.mjs +374 -0
- package/providers/codex/test/n0-guard-counterexamples.mjs +1068 -0
- package/providers/codex/test/n0-guard-independent-projection.mjs +70 -0
- package/providers/codex/test/n0-guard-independent-projection.test.mjs +53 -0
- package/providers/codex/test/n0-guard-manifest-author.mjs +424 -0
- package/providers/codex/test/n0-guard-mutation-operators.test.mjs +244 -0
- package/providers/codex/test/n0-guard-mutation-runner.mjs +599 -0
- package/providers/codex/test/n0-guard-remainder-classes.test.mjs +314 -0
- package/providers/codex/test/n0-guard-sanitizer-pairs.test.mjs +68 -0
- package/providers/codex/test/n0-guard-shared-context-predicates.test.mjs +84 -0
- package/providers/codex/test/n0-harness.test.mjs +553 -0
- package/providers/codex/test/n0-notable-regressions.test.mjs +70 -0
- package/providers/codex/test/n0-subject-boundary.mjs +97 -0
- package/providers/codex/test/n0-subject-boundary.test.mjs +72 -0
- package/providers/codex/test/same-chat-plan-preflight.sh +167 -0
- package/providers/codex/tools/refresh-manifest.mjs +81 -0
- package/providers/codex/wake-helper/TRANSPORT-NOTES.md +25 -0
- package/providers/codex/wake-helper/integration.test.mjs +285 -0
- package/providers/codex/wake-helper/kijito-wake-helper.mjs +488 -0
- package/providers/codex/wake-helper/kijito-wake-helper.test.mjs +128 -0
- package/providers/codex/wake-helper/mock-daemon.mjs +64 -0
- package/providers/codex/wake-helper/status-probe.mjs +45 -0
- package/providers/codex/wake-helper/ws-uds.mjs +153 -0
- package/providers/monitor/.github/workflows/publish-npm.yml +38 -0
- package/providers/monitor/.github/workflows/publish-pypi.yml +30 -0
- package/providers/monitor/CHANGELOG.md +465 -0
- package/providers/monitor/IMPORT-PROVENANCE.md +68 -0
- package/providers/monitor/LICENSE +202 -0
- package/providers/monitor/NOTICE +7 -0
- package/providers/monitor/OPAQUE-OUTPUT-ENFORCEMENT.md +45 -0
- package/providers/monitor/README.md +571 -0
- package/providers/monitor/RELEASING.md +125 -0
- package/providers/monitor/arm-hive-monitor.sh +13 -0
- package/providers/monitor/bin/cli.js +43 -0
- package/providers/monitor/com.kijito.inbox-monitor.plist.template +59 -0
- package/providers/monitor/docs/DESIGN.md +984 -0
- package/providers/monitor/kijito-inbox-monitor@.service.template +95 -0
- package/providers/monitor/kijito_inbox_monitor.py +3666 -0
- package/providers/monitor/package.json +35 -0
- package/providers/monitor/pyproject.toml +42 -0
- package/providers/monitor/scripts/mutation-check.py +399 -0
- package/providers/monitor/scripts/prepublish-gate.sh +284 -0
- 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).
|