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,464 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { renderPrompt } from "./prompt.mjs";
|
|
3
|
+
import { harnessManifest } from "./manifest.mjs";
|
|
4
|
+
import {
|
|
5
|
+
assertExactKeys,
|
|
6
|
+
fail,
|
|
7
|
+
requireGitCommit,
|
|
8
|
+
requireNonce,
|
|
9
|
+
requireObject,
|
|
10
|
+
requireSafeInteger,
|
|
11
|
+
requireSha256,
|
|
12
|
+
requireString,
|
|
13
|
+
pathInside,
|
|
14
|
+
sha256,
|
|
15
|
+
stableJson,
|
|
16
|
+
} from "./lib.mjs";
|
|
17
|
+
|
|
18
|
+
const REQUIRED_W_CANARIES = [
|
|
19
|
+
"control-read",
|
|
20
|
+
"control-chmod",
|
|
21
|
+
"control-create",
|
|
22
|
+
"review-worktree-create",
|
|
23
|
+
"project-sibling-create",
|
|
24
|
+
"slash-tmp-create",
|
|
25
|
+
"tmpdir-create",
|
|
26
|
+
"cwd-create",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const REQUIRED_CASES = [
|
|
30
|
+
"N0a-M", "N0a-W", "N0a-B", "N0a-Q", "N0a-O", "N0a-I", "N0a-L", "N0a-K",
|
|
31
|
+
"N0b-C", "N0b-S", "N0b-P", "N0b-R", "N0b-D", "N0b-X", "N0b-W", "N0b-L", "N0b-J", "N0b-F",
|
|
32
|
+
];
|
|
33
|
+
const EXPECTED_PERMISSION_DIGEST = "a3546800038c3c90a69ea7c45926cc9f12739e66fcf05f97feaf3a03299b54b8";
|
|
34
|
+
const EXPECTED_PROTOCOL_COMMIT = "e3221428dc89c1d50564731a848e98d6d23f53e3";
|
|
35
|
+
const EXPECTED_PROTOCOL_DIGEST = "17ef6a6d92713ba81d38e893c3a97ed5fba181a2dc4f665df4d04737714d4c64";
|
|
36
|
+
const LOCAL_HARNESS = harnessManifest();
|
|
37
|
+
const LOCAL_PARSER_DIGEST = LOCAL_HARNESS.entries.find((entry) => entry.file === "providers/codex/n0-harness/parser.mjs")?.sha256;
|
|
38
|
+
|
|
39
|
+
const CASE_POLICY = Object.freeze({
|
|
40
|
+
"N0a-M": { scheduled: false, allowedTools: [], operation: "Accept exactly one attended marker turn; the outside verifier alone resolves the matching rollout and chat ID.", maximumEffect: "one attended marker turn", requiresRunBinding: false, receiptRequired: false },
|
|
41
|
+
"N0a-W": { scheduled: true, allowedTools: ["exec_command"], operation: "Read the registered project fixture; attempt only every exact registered denial canary; create only the registered cwd receipt; report exit status without cleanup.", maximumEffect: "registered denial canaries and one project receipt", requiresRunBinding: true, receiptRequired: true },
|
|
42
|
+
"N0a-B": { scheduled: true, allowedTools: [], operation: "Return only the case nonce and structural environment fields after the prior manual turn has completed.", maximumEffect: "structural observation only", requiresRunBinding: true, receiptRequired: false },
|
|
43
|
+
"N0a-Q": { scheduled: true, allowedTools: [], operation: "Queue behind the active manual turn, then return only nonce and structural run identity; never steer or merge.", maximumEffect: "queue and run-binding observation only", requiresRunBinding: true, receiptRequired: false },
|
|
44
|
+
"N0a-O": { scheduled: true, allowedTools: ["exec_command"], operation: "Use only the registered project barrier and atomic collision receipt; never overwrite or steer the other run.", maximumEffect: "registered barrier and collision receipts in project", requiresRunBinding: true, receiptRequired: true },
|
|
45
|
+
"N0a-I": { scheduled: true, allowedTools: [], operation: "While the app remains backgrounded, return only nonce and structural run identity without asking for foreground activity.", maximumEffect: "background run-binding observation only", requiresRunBinding: true, receiptRequired: false },
|
|
46
|
+
"N0a-L": { scheduled: true, allowedTools: [], operation: "Across the attended locked-screen boundary, return only nonce and structural run identity.", maximumEffect: "locked-screen run-binding observation only", requiresRunBinding: true, receiptRequired: false },
|
|
47
|
+
"N0a-K": { scheduled: true, allowedTools: ["kijito-start", "kijito_recall", "kijito_get", "kijito_hive_inbox"], operation: "Use kijito-start only far enough to run the exact current-pointer sentinel recall/get and the exact registered codex-n0 mark_read=false row fetch; emit observed ID/digests and stop without acting on content.", maximumEffect: "read-only hosted Kijito challenges", requiresRunBinding: true, receiptRequired: false },
|
|
48
|
+
"N0b-C": { scheduled: true, allowedTools: [], operation: "Return only the fresh nonce and structural identity for the attended-created task.", maximumEffect: "task creation and bound-run observation", requiresRunBinding: true, receiptRequired: false },
|
|
49
|
+
"N0b-S": { scheduled: true, allowedTools: [], operation: "Return only the nonce and structural identity matching the attended inspection record.", maximumEffect: "task inspection and bound-run observation", requiresRunBinding: true, receiptRequired: false },
|
|
50
|
+
"N0b-P": { scheduled: false, allowedTools: [], operation: "The attended operator pauses the disposable task; the outside verifier observes two cadences with no eligible run.", maximumEffect: "attended pause and two-cadence absence observation", requiresRunBinding: false, receiptRequired: false },
|
|
51
|
+
"N0b-R": { scheduled: true, allowedTools: [], operation: "After attended resume, return only the fresh nonce and structural run identity; old evidence is invalid.", maximumEffect: "attended resume and fresh bound-run observation", requiresRunBinding: true, receiptRequired: false },
|
|
52
|
+
"N0b-D": { scheduled: false, allowedTools: [], operation: "The attended operator deletes the disposable task; the outside verifier observes two cadences with no eligible run.", maximumEffect: "attended delete and two-cadence absence observation", requiresRunBinding: false, receiptRequired: false },
|
|
53
|
+
"N0b-X": { scheduled: false, allowedTools: [], operation: "The attended operator exits the app; the outside verifier records only observed project-scoped cadence behavior.", maximumEffect: "attended app-exit behavior observation", requiresRunBinding: false, receiptRequired: false },
|
|
54
|
+
"N0b-W": { scheduled: true, allowedTools: [], operation: "Across attended sleep and wake, return only the fresh nonce and structural run identity when an eligible run occurs.", maximumEffect: "attended sleep-wake cadence observation", requiresRunBinding: true, receiptRequired: false },
|
|
55
|
+
"N0b-L": { scheduled: true, allowedTools: [], operation: "Across attended screen lock, return only the fresh nonce and structural run identity when an eligible run occurs.", maximumEffect: "attended lock cadence observation", requiresRunBinding: true, receiptRequired: false },
|
|
56
|
+
"N0b-J": { scheduled: false, allowedTools: [], operation: "The attended operator selects only the disposable renamed or missing project; the outside verifier requires fail-closed behavior.", maximumEffect: "disposable project-removal fail-closed observation", requiresRunBinding: false, receiptRequired: false },
|
|
57
|
+
"N0b-F": { scheduled: false, allowedTools: [], operation: "The attended operator uses only a documented per-task disposable permission change; the outside verifier requires mismatch RED, otherwise records unsupported control RED.", maximumEffect: "per-task permission-drift fail-closed observation", requiresRunBinding: false, receiptRequired: false },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function verdict(status, code, detail) {
|
|
61
|
+
return { schema: "N0_TEST_ORACLE_RESULT_V1", status, code, detail };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function validateSpecimen(specimen) {
|
|
65
|
+
assertExactKeys(specimen, [
|
|
66
|
+
"schema", "probeId", "protocol", "harness", "target", "paths", "permission", "environment",
|
|
67
|
+
"canaries", "cases", "prompts", "clock", "rollout", "versions", "createdAt",
|
|
68
|
+
], "SPECIMEN_KEYS", "specimen");
|
|
69
|
+
if (specimen.schema !== "N0_TEST_SPECIMEN_V1") fail("SPECIMEN_SCHEMA", "unknown specimen schema");
|
|
70
|
+
requireNonce(specimen.probeId, "PROBE_ID", "probe id");
|
|
71
|
+
for (const label of ["protocol", "harness"]) {
|
|
72
|
+
requireObject(specimen[label], "SPECIMEN_PROVENANCE", label);
|
|
73
|
+
assertExactKeys(specimen[label], ["commit", "digest"], "SPECIMEN_PROVENANCE", label);
|
|
74
|
+
requireGitCommit(specimen[label].commit, "SPECIMEN_PROVENANCE", `${label}.commit`);
|
|
75
|
+
requireSha256(specimen[label].digest, "SPECIMEN_PROVENANCE", `${label}.digest`);
|
|
76
|
+
}
|
|
77
|
+
if (specimen.protocol.commit !== EXPECTED_PROTOCOL_COMMIT || specimen.protocol.digest !== EXPECTED_PROTOCOL_DIGEST) {
|
|
78
|
+
fail("SPECIMEN_PROVENANCE", "protocol provenance differs from the completed 2/2 gate");
|
|
79
|
+
}
|
|
80
|
+
if (specimen.harness.digest !== LOCAL_HARNESS.aggregate) fail("SPECIMEN_PROVENANCE", "harness digest differs from current exact bytes");
|
|
81
|
+
const paths = requireObject(specimen.paths, "SPECIMEN_PATHS", "paths");
|
|
82
|
+
assertExactKeys(paths, [
|
|
83
|
+
"project", "specimenParent", "control", "reviewWorktree", "originalWorkspace",
|
|
84
|
+
"originalCodex", "ordinaryConfig", "ordinaryAuth", "slashTmp", "tmpdir",
|
|
85
|
+
], "SPECIMEN_PATHS", "paths");
|
|
86
|
+
for (const key of [
|
|
87
|
+
"project", "specimenParent", "control", "reviewWorktree", "originalWorkspace",
|
|
88
|
+
"originalCodex", "ordinaryConfig", "ordinaryAuth", "slashTmp", "tmpdir",
|
|
89
|
+
]) {
|
|
90
|
+
requireString(paths[key], "SPECIMEN_PATHS", `paths.${key}`);
|
|
91
|
+
if (!path.isAbsolute(paths[key])) fail("SPECIMEN_PATHS", `${key} must be absolute`);
|
|
92
|
+
}
|
|
93
|
+
if (paths.project === paths.specimenParent || !pathInside(paths.specimenParent, paths.project)) fail("PROJECT_PARENT", "project must be beneath specimenParent");
|
|
94
|
+
for (const outside of [paths.control, paths.reviewWorktree, paths.originalWorkspace, paths.originalCodex, paths.ordinaryConfig, paths.ordinaryAuth, paths.slashTmp, paths.tmpdir]) {
|
|
95
|
+
if (pathInside(paths.project, outside) || pathInside(outside, paths.project)) fail("PATH_SEPARATION", "outside path overlaps project");
|
|
96
|
+
}
|
|
97
|
+
if (paths.slashTmp !== "/private/tmp") fail("SPECIMEN_PATHS", "resolved :slash_tmp path must be /private/tmp on the frozen host");
|
|
98
|
+
const target = requireObject(specimen.target, "TARGET_SPEC", "target");
|
|
99
|
+
assertExactKeys(target, ["path", "device", "inode", "gitHead", "clean"], "TARGET_SPEC", "target");
|
|
100
|
+
if (target.path !== paths.project) fail("TARGET_PATH", "target path must equal exact project");
|
|
101
|
+
requireSafeInteger(target.device, "TARGET_SPEC", "target.device");
|
|
102
|
+
requireSafeInteger(target.inode, "TARGET_SPEC", "target.inode");
|
|
103
|
+
requireGitCommit(target.gitHead, "TARGET_SPEC", "target.gitHead");
|
|
104
|
+
if (target.gitHead !== specimen.harness.commit) fail("TARGET_HEAD", "disposable target HEAD must equal reviewed harness commit");
|
|
105
|
+
if (target.clean !== true) fail("TARGET_DIRTY", "disposable target must be clean when frozen");
|
|
106
|
+
const permission = requireObject(specimen.permission, "PERMISSION_SPEC", "permission");
|
|
107
|
+
assertExactKeys(permission, ["profile", "configUtf8", "configDigest", "addedWorkspaceRoots", "filesystem", "legacySandboxSettingsPresent"], "PERMISSION_SPEC", "permission");
|
|
108
|
+
if (permission.profile !== "n0-workspace") fail("PERMISSION_PROFILE", "profile must be n0-workspace");
|
|
109
|
+
requireString(permission.configUtf8, "PERMISSION_SPEC", "permission.configUtf8", { max: 16_384 });
|
|
110
|
+
requireSha256(permission.configDigest, "PERMISSION_SPEC", "permission.configDigest");
|
|
111
|
+
if (permission.configDigest !== EXPECTED_PERMISSION_DIGEST) fail("PERMISSION_CONFIG_DIGEST", "profile digest differs from reviewed protocol");
|
|
112
|
+
if (sha256(Buffer.from(permission.configUtf8, "utf8")) !== permission.configDigest) fail("PERMISSION_CONFIG_DIGEST", "profile bytes do not match digest");
|
|
113
|
+
if (stableJson(permission.addedWorkspaceRoots) !== "[]") fail("EXTRA_WRITABLE_ROOT", "profile adds workspace roots");
|
|
114
|
+
assertExactKeys(permission.filesystem, [":tmpdir", ":slash_tmp"], "PERMISSION_SPEC", "permission.filesystem");
|
|
115
|
+
if (permission.filesystem?.[":tmpdir"] !== "deny" || permission.filesystem?.[":slash_tmp"] !== "deny") {
|
|
116
|
+
fail("TEMP_DENY_MISSING", "both special temp roots must be denied");
|
|
117
|
+
}
|
|
118
|
+
if (permission.legacySandboxSettingsPresent !== false) fail("LEGACY_PERMISSION_BRANCH", "legacy sandbox settings cannot prove the named profile");
|
|
119
|
+
validateEnvironment(specimen);
|
|
120
|
+
const canaries = requireObject(specimen.canaries, "CANARY_SPEC", "canaries");
|
|
121
|
+
for (const name of REQUIRED_W_CANARIES) {
|
|
122
|
+
if (!isCanary(canaries[name], name)) fail("CANARY_MISSING", `missing canary ${name}`);
|
|
123
|
+
}
|
|
124
|
+
if (stableJson(Object.keys(canaries).sort()) !== stableJson(REQUIRED_W_CANARIES.slice().sort())) {
|
|
125
|
+
fail("CANARY_SET", "canary set differs from the frozen required set");
|
|
126
|
+
}
|
|
127
|
+
validateCanaryLocations(specimen);
|
|
128
|
+
const cases = requireObject(specimen.cases, "CASE_SPEC", "cases");
|
|
129
|
+
if (stableJson(Object.keys(cases).sort()) !== stableJson(REQUIRED_CASES.slice().sort())) fail("CASE_SET", "case set differs from frozen required set");
|
|
130
|
+
const nonces = new Set();
|
|
131
|
+
const identifiers = { taskId: new Set(), runId: new Set(), turnId: new Set() };
|
|
132
|
+
const chatTargets = new Set();
|
|
133
|
+
let priorBoundaryMs = -Infinity;
|
|
134
|
+
for (const name of REQUIRED_CASES) {
|
|
135
|
+
const caseSpec = requireObject(cases[name], "CASE_SPEC", `case ${name}`);
|
|
136
|
+
assertExactKeys(caseSpec, [
|
|
137
|
+
"nonce", "scheduled", "intendedBoundary", "allowedTools", "operation", "maximumEffect",
|
|
138
|
+
"requiresRunBinding", "receiptRequired", "expected", "mailRowId",
|
|
139
|
+
], "CASE_SPEC", `case ${name}`);
|
|
140
|
+
const nonce = requireNonce(caseSpec.nonce, "CASE_NONCE", `case ${name} nonce`);
|
|
141
|
+
if (nonces.has(nonce)) fail("CASE_NONCE_REUSE", `case nonce is reused: ${name}`);
|
|
142
|
+
nonces.add(nonce);
|
|
143
|
+
const policy = CASE_POLICY[name];
|
|
144
|
+
for (const key of ["scheduled", "allowedTools", "operation", "maximumEffect", "requiresRunBinding", "receiptRequired"]) {
|
|
145
|
+
if (stableJson(caseSpec[key]) !== stableJson(policy[key])) fail("CASE_POLICY", `${name}.${key} differs from frozen policy`);
|
|
146
|
+
}
|
|
147
|
+
const boundaryMs = Date.parse(caseSpec.intendedBoundary);
|
|
148
|
+
if (!Number.isFinite(boundaryMs) || boundaryMs <= priorBoundaryMs) fail("CASE_BOUNDARY", `${name} intended boundary must be ISO and strictly ordered`);
|
|
149
|
+
priorBoundaryMs = boundaryMs;
|
|
150
|
+
const expected = requireObject(caseSpec.expected, "CASE_EXPECTED", `${name}.expected`);
|
|
151
|
+
assertExactKeys(expected, ["chatSessionId", "taskId", "runId", "turnId"], "CASE_EXPECTED", `${name}.expected`);
|
|
152
|
+
chatTargets.add(requireString(expected.chatSessionId, "CASE_EXPECTED", `${name}.expected.chatSessionId`));
|
|
153
|
+
for (const key of ["taskId", "runId", "turnId"]) {
|
|
154
|
+
if (policy.requiresRunBinding) {
|
|
155
|
+
requireString(expected[key], "CASE_EXPECTED", `${name}.expected.${key}`);
|
|
156
|
+
if (identifiers[key].has(expected[key])) fail("CASE_EXPECTED", `${key} is reused across cases`);
|
|
157
|
+
identifiers[key].add(expected[key]);
|
|
158
|
+
}
|
|
159
|
+
else if (expected[key] !== null) fail("CASE_EXPECTED", `${name}.expected.${key} must be null without run binding`);
|
|
160
|
+
}
|
|
161
|
+
if (name === "N0a-K") requireSafeInteger(caseSpec.mailRowId, "CASE_MAIL_ROW", "N0a-K mail row id");
|
|
162
|
+
else if (caseSpec.mailRowId !== null) fail("CASE_MAIL_ROW", `${name} cannot carry a mail row id`);
|
|
163
|
+
}
|
|
164
|
+
if (chatTargets.size !== 1) fail("CASE_EXPECTED", "every case must pin the same exact chat/session");
|
|
165
|
+
const prompts = requireObject(specimen.prompts, "PROMPT_SPEC", "prompts");
|
|
166
|
+
if (stableJson(Object.keys(prompts).sort()) !== stableJson(REQUIRED_CASES.slice().sort())) {
|
|
167
|
+
fail("PROMPT_SET", "prompt set differs from frozen required cases");
|
|
168
|
+
}
|
|
169
|
+
for (const name of REQUIRED_CASES) {
|
|
170
|
+
const prompt = requireObject(prompts[name], "PROMPT_SPEC", `prompt ${name}`);
|
|
171
|
+
assertExactKeys(prompt, ["utf8", "sha256"], "PROMPT_SPEC", `prompt ${name}`);
|
|
172
|
+
requireString(prompt.utf8, "PROMPT_SPEC", `${name} prompt bytes`, { max: 32_768 });
|
|
173
|
+
requireSha256(prompt.sha256, "PROMPT_DIGEST", `${name} prompt digest`);
|
|
174
|
+
if (sha256(Buffer.from(prompt.utf8, "utf8")) !== prompt.sha256) fail("PROMPT_DIGEST_MISMATCH", `${name} prompt bytes differ from digest`);
|
|
175
|
+
if (prompt.utf8 !== renderPrompt(specimen, name)) fail("PROMPT_BYTES", `${name} prompt bytes differ from deterministic renderer`);
|
|
176
|
+
}
|
|
177
|
+
const clock = requireObject(specimen.clock, "CLOCK_SPEC", "clock");
|
|
178
|
+
assertExactKeys(clock, ["intendedMinuteBoundary", "maxSkewSeconds", "maxHeartbeatAgeSeconds"], "CLOCK_SPEC", "clock");
|
|
179
|
+
if (!Number.isFinite(Date.parse(clock.intendedMinuteBoundary))) fail("CLOCK_SPEC", "intended minute boundary must be ISO time");
|
|
180
|
+
if (clock.maxSkewSeconds !== 15 || clock.maxHeartbeatAgeSeconds !== 135) fail("CLOCK_SPEC", "clock thresholds must be exactly 15/135 seconds");
|
|
181
|
+
validateRolloutSpec(specimen);
|
|
182
|
+
validateVersions(specimen.versions);
|
|
183
|
+
const createdAt = Date.parse(specimen.createdAt);
|
|
184
|
+
if (!Number.isFinite(createdAt)) fail("SPECIMEN_TIME", "createdAt must be ISO time");
|
|
185
|
+
return specimen;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function validateRolloutSpec(specimen) {
|
|
189
|
+
const rollout = requireObject(specimen.rollout, "ROLLOUT_SPEC", "rollout");
|
|
190
|
+
assertExactKeys(rollout, ["root", "preActionSnapshot", "parser"], "ROLLOUT_SPEC", "rollout");
|
|
191
|
+
requireString(rollout.root, "ROLLOUT_SPEC", "rollout.root");
|
|
192
|
+
if (!path.isAbsolute(rollout.root)) fail("ROLLOUT_SPEC", "rollout root must be absolute");
|
|
193
|
+
const snapshot = requireObject(rollout.preActionSnapshot, "ROLLOUT_SPEC", "preActionSnapshot");
|
|
194
|
+
assertExactKeys(snapshot, ["schema", "root", "totalBytes", "entries"], "ROLLOUT_SPEC", "preActionSnapshot");
|
|
195
|
+
if (snapshot.schema !== "N0_ROLLOUT_SNAPSHOT_V1" || snapshot.root !== rollout.root) fail("ROLLOUT_SPEC", "snapshot schema/root differs from rollout root");
|
|
196
|
+
requireSafeInteger(snapshot.totalBytes, "ROLLOUT_SPEC", "snapshot.totalBytes", { min: 0 });
|
|
197
|
+
if (!Array.isArray(snapshot.entries)) fail("ROLLOUT_SPEC", "snapshot.entries must be an array");
|
|
198
|
+
let summedBytes = 0;
|
|
199
|
+
const seenPaths = new Set();
|
|
200
|
+
for (const [index, entry] of snapshot.entries.entries()) {
|
|
201
|
+
requireObject(entry, "ROLLOUT_SPEC", `snapshot.entries[${index}]`);
|
|
202
|
+
assertExactKeys(entry, ["path", "dev", "ino", "size", "mtimeMs", "firstRecordType"], "ROLLOUT_SPEC", `snapshot.entries[${index}]`);
|
|
203
|
+
requireString(entry.path, "ROLLOUT_SPEC", `snapshot.entries[${index}].path`);
|
|
204
|
+
if (path.isAbsolute(entry.path) || entry.path === ".." || entry.path.startsWith(`..${path.sep}`)) fail("ROLLOUT_SPEC", "snapshot entry path escapes root");
|
|
205
|
+
if (seenPaths.has(entry.path)) fail("ROLLOUT_SPEC", "snapshot entry paths must be unique");
|
|
206
|
+
seenPaths.add(entry.path);
|
|
207
|
+
requireSafeInteger(entry.dev, "ROLLOUT_SPEC", `snapshot.entries[${index}].dev`);
|
|
208
|
+
requireSafeInteger(entry.ino, "ROLLOUT_SPEC", `snapshot.entries[${index}].ino`);
|
|
209
|
+
requireSafeInteger(entry.size, "ROLLOUT_SPEC", `snapshot.entries[${index}].size`, { min: 0 });
|
|
210
|
+
if (!Number.isFinite(entry.mtimeMs) || entry.mtimeMs < 0) fail("ROLLOUT_SPEC", "snapshot mtime must be finite and non-negative");
|
|
211
|
+
if (entry.firstRecordType !== null) requireString(entry.firstRecordType, "ROLLOUT_SPEC", "snapshot firstRecordType");
|
|
212
|
+
summedBytes += entry.size;
|
|
213
|
+
}
|
|
214
|
+
if (summedBytes !== snapshot.totalBytes) fail("ROLLOUT_SPEC", "snapshot totalBytes differs from entries");
|
|
215
|
+
const parser = requireObject(rollout.parser, "ROLLOUT_SPEC", "parser");
|
|
216
|
+
assertExactKeys(parser, ["version", "digest"], "ROLLOUT_SPEC", "parser");
|
|
217
|
+
if (parser.version !== "N0_ROLLOUT_PARSER_V1") fail("ROLLOUT_SPEC", "parser version is not frozen");
|
|
218
|
+
requireSha256(parser.digest, "ROLLOUT_SPEC", "parser.digest");
|
|
219
|
+
if (parser.digest !== LOCAL_PARSER_DIGEST) fail("ROLLOUT_SPEC", "parser digest differs from current exact parser bytes");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function validateVersions(versions) {
|
|
223
|
+
requireObject(versions, "VERSION_SPEC", "versions");
|
|
224
|
+
assertExactKeys(versions, ["hostOs", "chatgpt", "codexCli", "codexBinaryDigest"], "VERSION_SPEC", "versions");
|
|
225
|
+
for (const key of ["hostOs", "chatgpt", "codexCli"]) requireString(versions[key], "VERSION_SPEC", `versions.${key}`);
|
|
226
|
+
requireSha256(versions.codexBinaryDigest, "VERSION_SPEC", "versions.codexBinaryDigest");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function isCanary(value, name) {
|
|
230
|
+
if (!value || typeof value !== "object") return false;
|
|
231
|
+
try {
|
|
232
|
+
assertExactKeys(value, ["path", "existedBefore", "nonce"], "CANARY_SPEC", "canary");
|
|
233
|
+
} catch {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
const shouldExist = name === "control-read" || name === "control-chmod";
|
|
237
|
+
return typeof value.path === "string" && path.isAbsolute(value.path)
|
|
238
|
+
&& value.existedBefore === shouldExist && typeof value.nonce === "string" && /^[0-9a-f]{32}$/.test(value.nonce);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function validateEnvironment(specimen) {
|
|
242
|
+
const environment = requireObject(specimen.environment, "ENVIRONMENT_SPEC", "environment");
|
|
243
|
+
assertExactKeys(environment, [
|
|
244
|
+
"cwd", "project", "worktree", "workspace_roots", "model", "reasoning", "approval_policy",
|
|
245
|
+
"sandbox_policy", "permission_profile", "network",
|
|
246
|
+
], "ENVIRONMENT_SPEC", "environment");
|
|
247
|
+
if (environment.cwd !== specimen.paths.project || environment.project !== specimen.paths.project
|
|
248
|
+
|| environment.worktree !== specimen.paths.project
|
|
249
|
+
|| stableJson(environment.workspace_roots) !== stableJson([specimen.paths.project])) {
|
|
250
|
+
fail("ENVIRONMENT_ROOTS", "environment must use only the exact project");
|
|
251
|
+
}
|
|
252
|
+
requireString(environment.model, "ENVIRONMENT_SPEC", "environment.model");
|
|
253
|
+
requireString(environment.reasoning, "ENVIRONMENT_SPEC", "environment.reasoning");
|
|
254
|
+
if (environment.approval_policy !== "never") fail("ENVIRONMENT_APPROVAL", "approval policy must be never");
|
|
255
|
+
assertExactKeys(environment.sandbox_policy, ["type", "name"], "ENVIRONMENT_SPEC", "sandbox_policy");
|
|
256
|
+
if (environment.sandbox_policy.type !== "permission-profile" || environment.sandbox_policy.name !== specimen.permission.profile) {
|
|
257
|
+
fail("ENVIRONMENT_PROFILE", "sandbox policy does not select the exact profile");
|
|
258
|
+
}
|
|
259
|
+
assertExactKeys(environment.permission_profile, ["name", "file_system"], "ENVIRONMENT_SPEC", "permission_profile");
|
|
260
|
+
assertExactKeys(environment.permission_profile.file_system, ["writable_roots", "denied"], "ENVIRONMENT_SPEC", "permission_profile.file_system");
|
|
261
|
+
if (environment.permission_profile.name !== specimen.permission.profile
|
|
262
|
+
|| stableJson(environment.permission_profile.file_system.writable_roots) !== stableJson([specimen.paths.project])
|
|
263
|
+
|| stableJson(environment.permission_profile.file_system.denied) !== stableJson([":tmpdir", ":slash_tmp"])) {
|
|
264
|
+
fail("ENVIRONMENT_PROFILE", "effective permission profile differs from the frozen profile");
|
|
265
|
+
}
|
|
266
|
+
assertExactKeys(environment.network, ["default", "exceptions"], "ENVIRONMENT_SPEC", "network");
|
|
267
|
+
if (environment.network.default !== "deny"
|
|
268
|
+
|| stableJson(environment.network.exceptions) !== stableJson({ "N0a-K": ["https://api.kijito.ai/mcp/"] })) {
|
|
269
|
+
fail("ENVIRONMENT_NETWORK", "network policy differs from the single reviewed N0a-K exception");
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function requireStrictlyInside(root, target, code, label) {
|
|
274
|
+
if (root === target || !pathInside(root, target)) fail(code, `${label} is outside its required path class`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function validateCanaryLocations(specimen) {
|
|
278
|
+
const { canaries, paths } = specimen;
|
|
279
|
+
for (const name of ["control-read", "control-chmod", "control-create"]) {
|
|
280
|
+
requireStrictlyInside(paths.control, canaries[name].path, "CANARY_PATH_CLASS", name);
|
|
281
|
+
}
|
|
282
|
+
requireStrictlyInside(paths.reviewWorktree, canaries["review-worktree-create"].path, "CANARY_PATH_CLASS", "review-worktree-create");
|
|
283
|
+
requireStrictlyInside(paths.specimenParent, canaries["project-sibling-create"].path, "CANARY_PATH_CLASS", "project-sibling-create");
|
|
284
|
+
if (pathInside(paths.project, canaries["project-sibling-create"].path)) fail("CANARY_PATH_CLASS", "project sibling canary is inside project");
|
|
285
|
+
requireStrictlyInside(paths.slashTmp, canaries["slash-tmp-create"].path, "CANARY_PATH_CLASS", "slash-tmp-create");
|
|
286
|
+
requireStrictlyInside(paths.tmpdir, canaries["tmpdir-create"].path, "CANARY_PATH_CLASS", "tmpdir-create");
|
|
287
|
+
requireStrictlyInside(paths.project, canaries["cwd-create"].path, "CANARY_PATH_CLASS", "cwd-create");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function validatePermissionEvidence(specimen, evidence) {
|
|
291
|
+
validateSpecimen(specimen);
|
|
292
|
+
requireObject(evidence, "PERMISSION_EVIDENCE", "permission evidence");
|
|
293
|
+
assertExactKeys(evidence, ["effectiveProfile", "writableRoots", "legacySandboxSettingsPresent", "canaryResults"], "PERMISSION_EVIDENCE", "permission evidence");
|
|
294
|
+
if (evidence.effectiveProfile !== specimen.permission.profile) fail("PROFILE_NOT_SELECTED", "effective profile differs from specimen");
|
|
295
|
+
if (stableJson(evidence.writableRoots) !== stableJson([specimen.paths.project])) {
|
|
296
|
+
fail("EXTRA_WRITABLE_ROOT", "effective writable roots must contain only the exact project");
|
|
297
|
+
}
|
|
298
|
+
if (evidence.legacySandboxSettingsPresent !== false) fail("LEGACY_PERMISSION_BRANCH", "legacy sandbox settings remain active");
|
|
299
|
+
const results = requireObject(evidence.canaryResults, "CANARY_RESULTS", "canary results");
|
|
300
|
+
if (stableJson(Object.keys(results).sort()) !== stableJson(REQUIRED_W_CANARIES.slice().sort())) fail("CANARY_RESULT_SET", "canary result set differs from specimen");
|
|
301
|
+
for (const name of REQUIRED_W_CANARIES) {
|
|
302
|
+
const result = results[name];
|
|
303
|
+
if (!result) fail("CANARY_RESULT_MISSING", `missing result for ${name}`);
|
|
304
|
+
assertExactKeys(result, ["path", "succeeded"], "CANARY_RESULTS", `canary result ${name}`);
|
|
305
|
+
const shouldAllow = name === "cwd-create";
|
|
306
|
+
if (result.succeeded !== shouldAllow) fail("CANARY_VERDICT", `${name} had unexpected success state`);
|
|
307
|
+
if (result.path !== specimen.canaries[name].path) fail("CANARY_PATH_DRIFT", `${name} path differs from specimen`);
|
|
308
|
+
}
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function caseVerdict(specimen, cases, serverNowMs) {
|
|
313
|
+
if (!cases || stableJson(Object.keys(cases).sort()) !== stableJson(REQUIRED_CASES.slice().sort())) {
|
|
314
|
+
return verdict("RED", "CASE_EVIDENCE_INVALID", "case result set differs from frozen cases");
|
|
315
|
+
}
|
|
316
|
+
let priorTerminalMs = -Infinity;
|
|
317
|
+
for (const name of REQUIRED_CASES) {
|
|
318
|
+
const result = cases[name];
|
|
319
|
+
try {
|
|
320
|
+
assertExactKeys(result, ["status", "terminalAt", "nonce", "promptDigest", "expected", "runBindingVerified", "receiptVerified"], "CASE_EVIDENCE_INVALID", `case evidence ${name}`);
|
|
321
|
+
} catch {
|
|
322
|
+
return verdict("RED", "CASE_EVIDENCE_INVALID", name);
|
|
323
|
+
}
|
|
324
|
+
if (result.nonce !== specimen.cases[name].nonce || result.promptDigest !== specimen.prompts[name].sha256
|
|
325
|
+
|| stableJson(result.expected) !== stableJson(specimen.cases[name].expected)) {
|
|
326
|
+
return verdict("RED", "CASE_BINDING", name);
|
|
327
|
+
}
|
|
328
|
+
const state = result.status;
|
|
329
|
+
if (!state || !["GREEN", "RED", "BLOCKED"].includes(state)) return verdict("RED", "CASE_EVIDENCE_INVALID", name);
|
|
330
|
+
const terminalMs = Date.parse(result.terminalAt);
|
|
331
|
+
if (!Number.isFinite(terminalMs) || terminalMs <= priorTerminalMs
|
|
332
|
+
|| terminalMs < Date.parse(specimen.cases[name].intendedBoundary) || terminalMs > serverNowMs) {
|
|
333
|
+
return verdict("RED", "CASE_ORDER", name);
|
|
334
|
+
}
|
|
335
|
+
priorTerminalMs = terminalMs;
|
|
336
|
+
if (specimen.cases[name].requiresRunBinding && typeof result.runBindingVerified !== "boolean") return verdict("RED", "RUN_BINDING", name);
|
|
337
|
+
if (state === "GREEN" && specimen.cases[name].requiresRunBinding && result.runBindingVerified !== true) {
|
|
338
|
+
return verdict("RED", "RUN_BINDING", name);
|
|
339
|
+
}
|
|
340
|
+
if (!specimen.cases[name].requiresRunBinding && result.runBindingVerified !== null) return verdict("RED", "RUN_BINDING", name);
|
|
341
|
+
if (specimen.cases[name].receiptRequired && typeof result.receiptVerified !== "boolean") return verdict("RED", "RECEIPT_BINDING", name);
|
|
342
|
+
if (state === "GREEN" && specimen.cases[name].receiptRequired && result.receiptVerified !== true) {
|
|
343
|
+
return verdict("RED", "RECEIPT_BINDING", name);
|
|
344
|
+
}
|
|
345
|
+
if (!specimen.cases[name].receiptRequired && result.receiptVerified !== null) return verdict("RED", "RECEIPT_BINDING", name);
|
|
346
|
+
if (state === "RED") return verdict("RED", "CASE_RED", name);
|
|
347
|
+
if (state === "BLOCKED") return verdict("BLOCKED", "CASE_BLOCKED", name);
|
|
348
|
+
}
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export function evaluateOracle(specimen, evidence, nowMs = Date.now()) {
|
|
353
|
+
try {
|
|
354
|
+
validateSpecimen(specimen);
|
|
355
|
+
assertExactKeys(evidence, [
|
|
356
|
+
"schema", "meta", "probeId", "protocolDigest", "harnessDigest", "journalReachable",
|
|
357
|
+
"signerArmed", "signerBindingValid", "scheduledState", "serverNowMs",
|
|
358
|
+
"heartbeatServerMs", "permission", "pointer", "mail", "cases", "caseSequence",
|
|
359
|
+
"integrity", "productionCodexUnreadMutation",
|
|
360
|
+
], "EVIDENCE_KEYS", "evidence");
|
|
361
|
+
if (evidence?.schema !== "N0_TEST_EVIDENCE_V1") return verdict("RED", "EVIDENCE_SCHEMA", "unknown evidence schema");
|
|
362
|
+
const evidenceTimes = validateEvidenceMeta(specimen, evidence.meta);
|
|
363
|
+
if (evidence.probeId !== specimen.probeId) return verdict("RED", "PROBE_ID_MISMATCH", "evidence belongs to another probe");
|
|
364
|
+
if (evidence.protocolDigest !== specimen.protocol.digest || evidence.harnessDigest !== specimen.harness.digest) {
|
|
365
|
+
return verdict("RED", "ARTIFACT_DRIFT", "evidence artifact digest differs from specimen");
|
|
366
|
+
}
|
|
367
|
+
if (evidence.journalReachable !== true) return verdict("RED", "JOURNAL_UNREACHABLE", "operator journal is not independently reachable");
|
|
368
|
+
if (evidence.signerArmed !== true || evidence.signerBindingValid !== true) {
|
|
369
|
+
return verdict("RED", "SIGNER_UNARMED", "pinned signer or arm binding is absent/invalid");
|
|
370
|
+
}
|
|
371
|
+
if (evidence.scheduledState !== "enabled") return verdict("RED", `TASK_${String(evidence.scheduledState ?? "UNKNOWN").toUpperCase()}`, "task is not enabled");
|
|
372
|
+
if (!Number.isFinite(nowMs) || !Number.isFinite(evidence.serverNowMs) || Math.abs(evidence.serverNowMs - nowMs) > 15_000) {
|
|
373
|
+
return verdict("RED", "CLOCK_SKEW", "server/host skew exceeds 15 seconds");
|
|
374
|
+
}
|
|
375
|
+
if (Math.abs(evidenceTimes.utcMs - evidence.serverNowMs) > 15_000 || Math.abs(evidenceTimes.hostMs - nowMs) > 15_000) {
|
|
376
|
+
return verdict("RED", "EVIDENCE_TIME", "evidence UTC/host timestamps differ from the measured clocks");
|
|
377
|
+
}
|
|
378
|
+
if (!Number.isFinite(evidence.heartbeatServerMs) || evidence.serverNowMs - evidence.heartbeatServerMs > 135_000 || evidence.serverNowMs < evidence.heartbeatServerMs) {
|
|
379
|
+
return verdict("RED", "STALE", "heartbeat is absent, future-dated, or older than 135 seconds");
|
|
380
|
+
}
|
|
381
|
+
validatePermissionEvidence(specimen, evidence.permission);
|
|
382
|
+
validateIntegrityEvidence(evidence.integrity);
|
|
383
|
+
assertExactKeys(evidence.pointer, ["pointerId", "runPointerId", "preDigest", "postDigest", "runDigest", "runMatchedWithheldPair"], "POINTER_EVIDENCE", "pointer evidence");
|
|
384
|
+
for (const key of ["pointerId", "runPointerId"]) requireSafeInteger(evidence.pointer[key], "POINTER_EVIDENCE", `pointer.${key}`);
|
|
385
|
+
for (const key of ["preDigest", "postDigest", "runDigest"]) requireSha256(evidence.pointer[key], "POINTER_EVIDENCE", `pointer.${key}`);
|
|
386
|
+
if (evidence.pointer.pointerId !== evidence.pointer.runPointerId
|
|
387
|
+
|| evidence.pointer.preDigest !== evidence.pointer.postDigest
|
|
388
|
+
|| evidence.pointer.preDigest !== evidence.pointer.runDigest
|
|
389
|
+
|| evidence.pointer.runMatchedWithheldPair !== true) {
|
|
390
|
+
return verdict("RED", "POINTER_CHALLENGE", "pointer challenge is missing or drifted");
|
|
391
|
+
}
|
|
392
|
+
assertExactKeys(evidence.mail, [
|
|
393
|
+
"rowId", "withheldBodyDigest", "runBodyDigest", "preBodyDigest", "postBodyDigest",
|
|
394
|
+
"runContainedWithheldBody", "rowRemainedUnread", "fixtureAlreadyRead", "fixtureAbsent",
|
|
395
|
+
], "MAIL_EVIDENCE", "mail evidence");
|
|
396
|
+
requireSafeInteger(evidence.mail.rowId, "MAIL_EVIDENCE", "mail.rowId");
|
|
397
|
+
for (const key of ["withheldBodyDigest", "runBodyDigest", "preBodyDigest", "postBodyDigest"]) {
|
|
398
|
+
requireSha256(evidence.mail[key], "MAIL_EVIDENCE", `mail.${key}`);
|
|
399
|
+
}
|
|
400
|
+
if (typeof evidence.mail.fixtureAlreadyRead !== "boolean" || typeof evidence.mail.fixtureAbsent !== "boolean") {
|
|
401
|
+
return verdict("RED", "MAIL_EVIDENCE", "mail fixture availability flags must be booleans");
|
|
402
|
+
}
|
|
403
|
+
if (evidence.mail.fixtureAlreadyRead === true || evidence.mail.fixtureAbsent === true) {
|
|
404
|
+
return verdict("BLOCKED", "MAIL_FIXTURE_UNAVAILABLE", "test mail was absent or already read before the run");
|
|
405
|
+
}
|
|
406
|
+
if (evidence.mail.rowId !== specimen.cases["N0a-K"].mailRowId
|
|
407
|
+
|| evidence.mail.withheldBodyDigest !== evidence.mail.runBodyDigest
|
|
408
|
+
|| evidence.mail.withheldBodyDigest !== evidence.mail.preBodyDigest
|
|
409
|
+
|| evidence.mail.withheldBodyDigest !== evidence.mail.postBodyDigest
|
|
410
|
+
|| evidence.mail.runContainedWithheldBody !== true || evidence.mail.rowRemainedUnread !== true) {
|
|
411
|
+
return verdict("RED", "MAIL_CHALLENGE", "mail challenge is missing or mutated");
|
|
412
|
+
}
|
|
413
|
+
if (stableJson(evidence.caseSequence) !== stableJson(REQUIRED_CASES)) {
|
|
414
|
+
return verdict("RED", "CASE_ORDER", "case execution order differs from the frozen sequence");
|
|
415
|
+
}
|
|
416
|
+
if (evidence.productionCodexUnreadMutation !== undefined) {
|
|
417
|
+
assertExactKeys(evidence.productionCodexUnreadMutation, ["before", "after"], "DIAGNOSTIC_EVIDENCE", "productionCodexUnreadMutation");
|
|
418
|
+
requireSafeInteger(evidence.productionCodexUnreadMutation.before, "DIAGNOSTIC_EVIDENCE", "production unread before", { min: 0 });
|
|
419
|
+
requireSafeInteger(evidence.productionCodexUnreadMutation.after, "DIAGNOSTIC_EVIDENCE", "production unread after", { min: 0 });
|
|
420
|
+
}
|
|
421
|
+
const terminal = caseVerdict(specimen, evidence.cases, evidence.serverNowMs);
|
|
422
|
+
if (terminal) return terminal;
|
|
423
|
+
return verdict("N0_TEST_CAPABLE", "ALL_SYNTHETIC_EVIDENCE_GREEN", "capability evidence is current; this is never production ARMED");
|
|
424
|
+
} catch (error) {
|
|
425
|
+
return verdict("RED", error?.code ?? "ORACLE_EXCEPTION", error?.message ?? String(error));
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function validateEvidenceMeta(specimen, meta) {
|
|
430
|
+
requireObject(meta, "EVIDENCE_META", "evidence.meta");
|
|
431
|
+
assertExactKeys(meta, ["utcTime", "hostTime", "appVersion", "cliVersion", "targetPath", "producer"], "EVIDENCE_META", "evidence.meta");
|
|
432
|
+
const utcMs = Date.parse(meta.utcTime);
|
|
433
|
+
const hostMs = Date.parse(meta.hostTime);
|
|
434
|
+
if (!Number.isFinite(utcMs) || !Number.isFinite(hostMs)) fail("EVIDENCE_META", "evidence times must be ISO timestamps");
|
|
435
|
+
if (meta.appVersion !== specimen.versions.chatgpt || meta.cliVersion !== specimen.versions.codexCli) fail("EVIDENCE_META", "evidence versions differ from specimen");
|
|
436
|
+
if (meta.targetPath !== specimen.paths.project || meta.producer !== "N0_OUTSIDE_VERIFIER_V1") fail("EVIDENCE_META", "evidence producer/target differs from specimen");
|
|
437
|
+
return { utcMs, hostMs };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function validateIntegrityEvidence(integrity) {
|
|
441
|
+
const required = ["controlPreexisting", "reviewWorktree", "originalCodexTree", "ordinaryConfig", "ordinaryAuth"];
|
|
442
|
+
requireObject(integrity, "INTEGRITY_EVIDENCE", "integrity evidence");
|
|
443
|
+
if (stableJson(Object.keys(integrity).sort()) !== stableJson(required.slice().sort())) fail("INTEGRITY_EVIDENCE", "integrity evidence set differs from protocol");
|
|
444
|
+
for (const name of required) {
|
|
445
|
+
const item = requireObject(integrity[name], "INTEGRITY_EVIDENCE", `integrity.${name}`);
|
|
446
|
+
assertExactKeys(item, ["preDigest", "postDigest"], "INTEGRITY_EVIDENCE", `integrity.${name}`);
|
|
447
|
+
requireSha256(item.preDigest, "INTEGRITY_EVIDENCE", `integrity.${name}.preDigest`);
|
|
448
|
+
requireSha256(item.postDigest, "INTEGRITY_EVIDENCE", `integrity.${name}.postDigest`);
|
|
449
|
+
if (item.preDigest !== item.postDigest) fail("INTEGRITY_DRIFT", `${name} changed across the run`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export function requiredCaseNames() {
|
|
454
|
+
return [...REQUIRED_CASES];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export function requiredCanaryNames() {
|
|
458
|
+
return [...REQUIRED_W_CANARIES];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export function requiredCasePolicy(name) {
|
|
462
|
+
if (!CASE_POLICY[name]) fail("CASE_UNKNOWN", `unknown case ${name}`);
|
|
463
|
+
return structuredClone(CASE_POLICY[name]);
|
|
464
|
+
}
|