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,472 @@
1
+ # Codex same-chat continuation N0 capability-probe protocol
2
+
3
+ Status: **PROTOCOL DRAFT — NO PROBE ACTION AUTHORIZED.**
4
+
5
+ This protocol operationalizes only N0a and N0b of
6
+ `same-chat-continuation-plan.md`. It is not provider code, a task prompt, a doctor implementation,
7
+ an installation, or permission to create a Scheduled task. Assay must complete two consecutive
8
+ zero-finding reviews from different angles on the exact unchanged protocol digest before Codex
9
+ prepares a test harness or asks Jason to perform the first attended marker action. Assay must then
10
+ complete the same two-consecutive-review gate on the frozen harness before any Scheduled task is
11
+ created.
12
+
13
+ Plan authority:
14
+
15
+ - PR #6 plan commit: `68a2ad85acac88fbfa785bd3026575a73b156f80`
16
+ - reviewed plan SHA-256: `3d11d5e0defd248e3a26d12ddb073fdd337acf8f38df785a204fbd2e0f22afac`
17
+ - Assay plan reviews: round 6 CLEAN at `0ec3f8b`, round 7 CLEAN at `247b31d`
18
+ - authority opened by those reviews: disposable N0a/N0b capability probes only
19
+
20
+ Current pre-probe observations, recorded 2026-07-30:
21
+
22
+ - macOS `26.4.1` (`25E253`)
23
+ - ChatGPT desktop `26.721.30844` build `5813`
24
+ - bundled `codex-cli 0.145.0`
25
+ - installed `0.145.0` permission branch measured before this revision: `codex sandbox -P :workspace`
26
+ remained able to write cwd, `/private/tmp`, and `$TMPDIR` even when legacy
27
+ `sandbox_workspace_write.exclude_*` overrides were supplied, so that combination is rejected;
28
+ a named profile extending `:workspace` with `:tmpdir = "deny"` and `:slash_tmp = "deny"` kept the
29
+ disposable cwd write GREEN and made both temp canaries fail `Operation not permitted` (exit 1)
30
+ - the CLI result proves the installed local sandbox primitive only; N0a-W must independently prove
31
+ that the ChatGPT Scheduled surface selects and preserves the same named profile, otherwise RED
32
+ - the loaded user config currently has SHA-256
33
+ `8bd92cc0d0305e71f81bf11ed196c3f2bd2461954260b113c4ea19fdf7f2f7fc` and contains legacy
34
+ `sandbox_mode = "workspace-write"`; no managed `allowed_permission_profiles` source was found
35
+ - official `0.145.0` documentation says any loaded `sandbox_mode` selects the legacy sandbox branch,
36
+ so the project-local profile is not presumed effective: if the desktop does not expose and apply
37
+ `n0-workspace` as a named/custom session permission, N0a-W is RED before any Scheduled task runs;
38
+ ordinary config is not edited to manufacture eligibility
39
+ - current official Codex manual SHA-256
40
+ `69bac3511f1d13a49b9a70bcafa9eae04e18376b65c17368e483dfc26c50e89d`
41
+ - the manual says an in-chat Scheduled task returns to the same chat with its existing context,
42
+ supports minute intervals, can use chat-available skills/plugins, and runs unattended with the
43
+ selected local project/worktree and default sandbox; these are claims to test, not evidence that
44
+ this installation satisfies N0
45
+ - legacy PID `38082` is alive and intentionally untouched; it is the withdrawn dedicated-thread
46
+ notifier and never counts as same-chat evidence
47
+
48
+ ## 1. Binary verdict and stop rules
49
+
50
+ N0 has two independent verdicts, `N0A_GREEN|RED|BLOCKED` and
51
+ `N0B_GREEN|RED|BLOCKED`. `N0_GREEN` requires both GREEN on one frozen protocol and one frozen harness.
52
+ `BLOCKED` is never converted to GREEN by explanation, self-report, screenshot, or partial evidence.
53
+
54
+ A mandatory case is RED when the installed product behaves contrary to the reviewed plan. It is
55
+ BLOCKED only when an attended prerequisite was not performed or an external outage prevents a
56
+ measurement. A procedural defect in the harness invalidates the affected run, changes the harness
57
+ digest, and returns to Assay review before a fresh nonce and retry. Product RED is not retried under a
58
+ different prompt until a new reviewed protocol explains why the changed prompt still tests the same
59
+ property.
60
+
61
+ Stop immediately and preserve read-only evidence if any of these occurs:
62
+
63
+ - a task or run cannot be tied to the marker-pinned exact chat `T` by an app-owned artifact outside
64
+ the run;
65
+ - the verifier finds zero or multiple marker matches;
66
+ - a run starts in a new, standalone, newest, visually similar, or controller-owned chat;
67
+ - task creation, inspection, pause, resume, or deletion would require UI/accessibility automation,
68
+ a private protocol, transcript mtime guessing, `thread/resume`, `thread/injectItems`, or steering;
69
+ - the effective cwd, project/worktree, model, sandbox, approval mode, permission profile, skill/plugin,
70
+ or MCP availability differs from the pre-registered specimen;
71
+ - a scheduled run mutates hive unread state, uses persona `codex` for anything except the one allowed
72
+ `mark_read=false` capability peek, or reads a mail body into the task prompt;
73
+ - the exact Scheduled run record, run/turn ID, or independent rollout evidence required by the plan
74
+ is unavailable;
75
+ - the legacy controller, its lock, ordinary Codex config/auth, production Kijito state, or a
76
+ non-disposable workspace would be changed.
77
+
78
+ No result from this protocol authorizes provider/server implementation, N1-N3, merge, install,
79
+ migration, River's provider-neutral API work, legacy-controller restart, or production cutover.
80
+
81
+ ## 2. Roles, isolation, and evidence custody
82
+
83
+ The attended operator performs only documented ChatGPT desktop controls and explicitly identified
84
+ screen-lock/sleep actions. Codex prepares prompts and read-only verifier commands but never drives the
85
+ UI. The outside verifier is a separate local process that is not a Scheduled run and does not use the
86
+ run's prose as evidence.
87
+
88
+ The PR review worktree is never the Scheduled task's project and is never inside that task's intended
89
+ writable scope. A newly created specimen has two separated roots:
90
+
91
+ - `/Users/jason/N0-Probes/<probe-id>/project/` is a dedicated detached disposable git worktree
92
+ created from the reviewed commit solely for N0; its whole
93
+ `/Users/jason/N0-Probes/<probe-id>/` tree is treated as disposable, contains no user work, and the
94
+ exact `project/` path is selected for every task;
95
+ - `/Users/jason/.local/state/codex-n0/<probe-id>/` is an owner-only outside-verifier control root,
96
+ created specifically for this probe, outside the task's selected project and ordinary temporary
97
+ writable roots, and holds the frozen specimen and evidence.
98
+
99
+ Owner-only mode is not treated as separation because the Scheduled process runs as the same OS user.
100
+ Across every eligible run the outside verifier seals the control directory non-traversable (`000`),
101
+ keeps comparison state in memory or through already-open descriptors, and restores access only after
102
+ the run is terminal. No secret-readable window may overlap task eligibility. N0a-W must prove the
103
+ Scheduled run cannot read a named control canary or chmod, replace, rename, delete, or create anything
104
+ in the sealed root. Any read or metadata/write success is RED.
105
+
106
+ Before relying on this separation, a negative preflight must prove that a run with the exact selected
107
+ project and sandbox can read the registered fixture in the disposable project but cannot read or
108
+ change the sealed control canary and cannot create a randomized canary in the control root or PR
109
+ review worktree. It must also prove every pre-existing file there, the ordinary Codex config/auth
110
+ hashes, and the original
111
+ `/Users/jason/Code/SideProjects/Codex/.codex/` tree remained unchanged. Any write to a non-disposable
112
+ path succeeds => RED and all later cases stop. Prompt text and what the run happened to touch are not
113
+ the boundary: verdicts use the effective granted profile plus denial probes. Any granted writable
114
+ root other than the exact disposable `project/` is RED.
115
+
116
+ The disposable project contains a pre-registered named permission profile `n0-workspace` that extends
117
+ `:workspace`, grants no additional workspace root, and sets both `:tmpdir` and `:slash_tmp` to `deny`.
118
+ The attended operator must be able to select that exact named/custom profile in the desktop
119
+ permissions control before task creation. The profile lives only in the trusted disposable project's
120
+ `.codex/config.toml`; ordinary Codex configuration is not changed. Because the current user config
121
+ selects the mutually exclusive legacy sandbox branch, merely defining the project profile is not
122
+ evidence that it is active. Absence of the named/custom selection, a warning that it was ignored, or
123
+ effective evidence that remains on legacy `workspace-write` is an immediate N0a-W RED and no
124
+ Scheduled task is created. If selection is available, N0a-W must still prove the effective run names
125
+ that exact profile, keeps the exact cwd writable, and denies randomized canaries in both
126
+ `/private/tmp` and the host `$TMPDIR`. Ignoring either deny, adding another writable root, or making
127
+ the cwd unwritable is RED.
128
+
129
+ The successful installed-CLI primitive measurement used these exact UTF-8 `config.toml` bytes
130
+ (including the final newline), SHA-256
131
+ `a3546800038c3c90a69ea7c45926cc9f12739e66fcf05f97feaf3a03299b54b8`:
132
+
133
+ ```toml
134
+ default_permissions = "n0-workspace"
135
+
136
+ [permissions.n0-workspace]
137
+ extends = ":workspace"
138
+
139
+ [permissions.n0-workspace.filesystem]
140
+ ":tmpdir" = "deny"
141
+ ":slash_tmp" = "deny"
142
+ ```
143
+
144
+ The isolated layout placed that file at
145
+ `/Users/jason/Code/SideProjects/Codex/.qa-tmp/n0-advp-round4/codex-home/config.toml` and used the
146
+ separate non-temp workspace
147
+ `/Users/jason/Code/SideProjects/Codex/.qa-tmp/n0-advp-round4/project/`. The tested binary was
148
+ `/Users/jason/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/bin/codex`, reported
149
+ `codex-cli 0.145.0`, and had SHA-256
150
+ `1da3f4e0e96028b8a771814293c3033dafd1971f943f6c7e79b0897fe705f590`. The exact three commands were:
151
+
152
+ ```sh
153
+ /usr/bin/env CODEX_HOME=/Users/jason/Code/SideProjects/Codex/.qa-tmp/n0-advp-round4/codex-home /Users/jason/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/bin/codex sandbox -P n0-workspace -C /Users/jason/Code/SideProjects/Codex/.qa-tmp/n0-advp-round4/project -- /usr/bin/touch /Users/jason/Code/SideProjects/Codex/.qa-tmp/n0-advp-round4/project/inside
154
+ /usr/bin/env CODEX_HOME=/Users/jason/Code/SideProjects/Codex/.qa-tmp/n0-advp-round4/codex-home /Users/jason/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/bin/codex sandbox -P n0-workspace -C /Users/jason/Code/SideProjects/Codex/.qa-tmp/n0-advp-round4/project -- /usr/bin/touch /private/tmp/n0-advp-round4-slash-tmp
155
+ /usr/bin/env CODEX_HOME=/Users/jason/Code/SideProjects/Codex/.qa-tmp/n0-advp-round4/codex-home /Users/jason/.codex/packages/standalone/releases/0.145.0-aarch64-apple-darwin/bin/codex sandbox -P n0-workspace -C /Users/jason/Code/SideProjects/Codex/.qa-tmp/n0-advp-round4/project -- /usr/bin/touch /var/folders/z2/qvgc70gs2sl87pkrd4fffnk40000gn/T/n0-advp-round4-envtmp
156
+ ```
157
+
158
+ The cwd command exited `0`; the `/private/tmp` and resolved `$TMPDIR` commands each exited `1` with
159
+ `Operation not permitted`, and neither outside canary existed afterward. Both `CODEX_HOME` and `-C`
160
+ were outside the two special temp roots; implementations with either root inside a temp grant are not
161
+ equivalent reproductions. These commands prove only the isolated CLI primitive. The current desktop
162
+ and Scheduled path remains subject to the separate effective-profile RED gates above.
163
+
164
+ Both roots must contain no symlink except normal Git-managed worktree metadata, and the disposable
165
+ worktree must be removed from every project/task selector after the probe. No real auth file, API
166
+ token, production mailbox body, whole transcript, or ordinary Codex configuration is copied into
167
+ either root. The evidence pack may retain only:
168
+
169
+ - file identity and bounded structural metadata for rollout candidates;
170
+ - the exact matching marker and run nonces;
171
+ - parsed `session_meta.payload.id`, run/turn/task IDs, timestamps, and environment/permission fields;
172
+ - prompt and artifact digests;
173
+ - bounded receipts created inside the disposable `project/` root;
174
+ - redacted screenshots of attended Scheduled controls when no machine-readable management artifact
175
+ exists, labeled `SETUP_EVIDENCE_ONLY`;
176
+ - command/version output and the verifier's deterministic verdicts.
177
+
178
+ Every evidence record contains `probe_id`, protocol digest, harness commit/digest, UTC time, host time,
179
+ the app and CLI versions, the target path, and the evidence producer. Hashes are computed only after
180
+ files stop changing. A manifest records each evidence file's relative path, size, and SHA-256. The
181
+ Scheduled principal cannot write the manifest or verifier verdicts. Assay receives the manifest plus
182
+ the minimal evidence needed to reproduce verdicts, never secrets.
183
+
184
+ The probe uses the stable dedicated test persona `codex-n0`; it does not create a new persona for each
185
+ run. It never creates or changes a memory, claim, checkpoint, or mail row owned by persona `codex`.
186
+ N0a-K may use persona `codex` only for the plan-authorized current-pointer/tool-reachability peek with
187
+ `mark_read=false`; any returned body remains untrusted data, the run takes no action from it, and that
188
+ peek is not the unread-mutation proof or dependent on unread state.
189
+ The mutation proof uses an exact synthetic unread row self-sent by and owned by `codex-n0`, so legacy
190
+ persona-`codex` consumers cannot alter it. If the hosted surface cannot create that isolated fixture
191
+ without another production persona, the case is BLOCKED rather than weakening attribution.
192
+
193
+ The outside verifier, not this chat or the Scheduled run, generates the test row's 128-bit body nonce
194
+ in process memory and sends the fixture without emitting that nonce to stdout, model context, the
195
+ task prompt, the disposable project, or pre-run evidence shown in the chat. Only its SHA-256 is
196
+ persisted in the sealed control root; the raw nonce is never written locally before the run. The body
197
+ nonce is distinct from the case/run nonce. The expected current pointer is selected immediately
198
+ before N0a-K by running the installed skill's exact sentinel recall outside the Scheduled run:
199
+ `kijito_recall(query="CODEX_CURRENT_STATE_POINTER_V1 RESUME NOW", scope="project", project="Codex",
200
+ full=true)`. The same query is pinned in `providers/codex/skills/kijito-start/SKILL.md` line 19 of the
201
+ frozen commit. The verifier requires one unambiguous top live result, freezes that exact memory ID,
202
+ and hashes the canonical UTF-8 content returned by `kijito_get(id)`. The parser ignores edge-preview
203
+ blocks and requires the final main `UNTRUSTED` block to name that exact requested ID and
204
+ `persona:codex`; it hashes only the body bytes between that block's fences, excluding the per-response
205
+ fence nonce. Zero or multiple main matches invalidates the snapshot. The ID and digest live only in
206
+ the sealed control root; neither is written into this protocol, prompt, project, or chat before the
207
+ run. The outside verifier re-fetches that exact ID after the run and requires the same digest. A
208
+ pre/post mismatch invalidates the case and requires a fresh snapshot; this protocol claims no Kijito
209
+ lease primitive.
210
+
211
+ ## 3. Frozen specimen and outside-verifier contract
212
+
213
+ Before the first attended action, the reviewed harness must generate and freeze `specimen.json` in
214
+ the outside-verifier control root with:
215
+
216
+ - a random 128-bit `probe_id` and separate verifier-generated 128-bit nonces for every case;
217
+ - exact disposable target worktree path, device/inode, git HEAD, clean status, and permission profile;
218
+ - exact PR review-worktree and verifier-control paths that must remain outside the task's writable
219
+ roots, plus the named write-denial canaries used to prove the separation;
220
+ - model and reasoning selection, sandbox mode, approval mode, network allowance, and permitted tools;
221
+ - exact `n0-workspace` profile bytes/digest, resolved `:slash_tmp` and `:tmpdir` paths, zero additional
222
+ workspace roots, and the registered cwd/temp/sibling denial canaries;
223
+ - the full candidate task prompt bytes and SHA-256 for every scheduled case;
224
+ - the intended minute boundary and acceptable host/server skew;
225
+ - the pre-action rollout snapshot and the bounded parser version/digest;
226
+ - explicit expected IDs that are known before each run; a run may fill only fields that cannot exist
227
+ before scheduling, and the attended operator must record those through documented controls before
228
+ the first eligible run.
229
+
230
+ The rollout snapshot walks only real, owner-matching files beneath the documented app-owned Codex
231
+ session root. It records path, device, inode, size, and first-record structural type without treating
232
+ mtime or newest-path order as identity. After Jason submits a marker, the verifier searches only files
233
+ created or advanced beyond the frozen snapshot, within a bounded byte range. Exactly one candidate
234
+ must contain the exact marker nonce in a user turn and must begin with a valid
235
+ `session_meta.payload.id`. That ID is `T`. The marker proves only `T`; it does not prove a scheduled
236
+ run belongs to `T`.
237
+
238
+ For every scheduled run, the outside verifier independently requires all of:
239
+
240
+ 1. the Scheduled control record captured by the attended operator identifies the pre-registered task
241
+ and run;
242
+ 2. one app-owned rollout beginning with `session_meta.payload.id == T` contains the corresponding
243
+ Scheduled run/turn identity and pre-registered run nonce;
244
+ 3. the rollout's cwd/project/worktree, model, sandbox, approval, and permission evidence equals the
245
+ frozen specimen;
246
+ 4. any disposable receipt is attributable to the same run/turn ID and nonce;
247
+ 5. the run neither steered an active turn nor relied on its own statement that it was in the right
248
+ chat.
249
+
250
+ If the installed build exposes no machine-readable Scheduled run record or no independent binding
251
+ between that record and the app-owned rollout, the case is RED. A screenshot can establish what the
252
+ operator selected, but cannot replace the run binding.
253
+
254
+ ## 4. N0a case matrix
255
+
256
+ Each case uses a fresh nonce. A prior run, duplicate Scheduled firing, or late completion bearing an
257
+ old nonce cannot satisfy a later case.
258
+
259
+ ### N0a-M — marker pins the exact chat
260
+
261
+ 1. Freeze the rollout snapshot and marker nonce outside ChatGPT.
262
+ 2. Ask Jason to submit exactly one attended marker turn in the intended existing chat.
263
+ 3. Resolve `T` with the outside-verifier contract above.
264
+
265
+ GREEN requires exactly one matching rollout and one `session_meta.payload.id`. This is the first user
266
+ action and is requested only after protocol and harness QA are CLEAN.
267
+
268
+ ### N0a-W — disposable write-boundary proof
269
+
270
+ Before trusting any later case, a scheduled run bound to `T` reads the registered fixture in
271
+ disposable project, attempts to read and chmod one named sealed-control canary, and attempts only
272
+ randomized, previously nonexistent canary paths in the control root, PR review worktree, a sibling of
273
+ the exact disposable project, `/private/tmp`, and the resolved host `$TMPDIR`. Every control
274
+ read/metadata/write attempt and every out-of-project create must be denied while one registered cwd
275
+ receipt succeeds. The outside verifier hashes all pre-existing config/auth, original-workspace
276
+ `.codex`, and review-worktree files before and after and requires them unchanged; no write is attempted
277
+ against a real config/auth path. Verdict is based on the granted `n0-workspace` profile and every
278
+ canary, not on a claim that an untested broad root happened to remain untouched. If an out-of-project
279
+ canary is unexpectedly created, the outside verifier records RED, removes only that exact randomized
280
+ canary after ownership/path verification, and stops N0. The probe never attempts to replace, rename,
281
+ or delete a real config, auth, source, or evidence file.
282
+
283
+ ### N0a-B — baseline after a completed manual turn
284
+
285
+ Create a disposable one-minute in-chat task through documented controls. Its frozen prompt returns
286
+ the case nonce and reads only structural evidence needed to prove the specimen environment. It makes
287
+ no file, mailbox, configuration, or external-state mutation. Run identity and every environment field
288
+ must match `T` and the specimen. Any standalone/new chat or unavailable run binding is RED.
289
+
290
+ ### N0a-Q — scheduled input during an active manual turn
291
+
292
+ Jason starts a bounded manual turn in `T` so it spans the next registered minute boundary. The
293
+ scheduled task becomes eligible during that turn. GREEN requires the scheduled input to queue, start
294
+ only after the manual turn releases ownership, retain its own registered run identity, and show no
295
+ `turn/steer` or merged disposition. Overlap, steering, lost run, or wrong chat is RED.
296
+
297
+ ### N0a-O — two tasks forced to overlap
298
+
299
+ Two disposable in-chat tasks target the same registered minute. Task A owns a pre-created disposable
300
+ 75-second barrier; task B becomes eligible while A is active. The barrier and atomic collision receipt
301
+ are test fixtures, not provider code, and the 75-second duration is the plan's explicit synthetic
302
+ exception to the 45-second production slice cap.
303
+
304
+ GREEN requires exactly one task to hold the fixture's winner token and act, while the other produces
305
+ an independently attributable collision receipt without steering, overwriting the winner, or making
306
+ a disposition. The verifier must distinguish provider scheduling from the fixture's own exclusion;
307
+ the forced concurrent eligibility is expected, but both tasks performing the registered action or
308
+ either task steering/overwriting is RED even when a later file-state snapshot looks singular.
309
+
310
+ ### N0a-I — inactive/backgrounded desktop
311
+
312
+ With the app running but backgrounded and Jason inactive for at least ten minutes, one pre-registered
313
+ run must start within 90 seconds of eligibility and satisfy the full run binding. Foregrounding the app
314
+ or manually prompting to make it start invalidates the case.
315
+
316
+ ### N0a-L — locked screen
317
+
318
+ With the computer awake, app running, and screen locked across the eligible boundary, one
319
+ pre-registered run must start within 90 seconds and satisfy the full binding. The operator records
320
+ lock and unlock times; visual notification alone is not evidence.
321
+
322
+ ### N0a-K — installed Kijito reachability and read-only inbox
323
+
324
+ In a second disposable run, explicitly invoke installed `kijito-start` only far enough to prove that
325
+ the Scheduled surface can use the installed skill/plugin/MCP, reach hosted `https://api.kijito.ai/mcp/`,
326
+ return the current pointer ID plus canonical content digest, and perform the allowed persona-`codex`
327
+ `mark_read=false` capability peek without acting on returned data. The outside verifier compares the
328
+ run's pointer pair to the withheld control-root snapshot; no constant from the prompt/project can
329
+ satisfy it.
330
+
331
+ Separately, the outside fixture creates one exact unread row containing the withheld body nonce by
332
+ self-sending from `codex-n0` to `codex-n0` before the run. The prompt may contain the row ID but never
333
+ the body nonce. The Scheduled run exact-fetches that row using `before_id = row_id + 1`, `limit = 1`,
334
+ and `mark_read=false`, rejects any returned ID other than the registered row, and its app-owned rollout
335
+ must contain the exact fetched body nonce. The outside verifier exact-fetches the row with the same
336
+ cursor plus `unread_only=true, mark_read=false` before and after and requires the same ID/body digest
337
+ to remain unread. Merely showing unchanged unread state without the fetched nonce in the run artifact
338
+ is RED. Only after the evidence manifest freezes may the outside fixture mark that test row read as
339
+ cleanup.
340
+
341
+ N0a-K is GREEN only when both independent challenges pass: the run's pointer ID/content-digest pair
342
+ equals the withheld single-writer snapshot, and the rollout contains fetched row content whose
343
+ SHA-256 equals the withheld body-nonce digest. Either proof alone is insufficient.
344
+
345
+ The task must not follow the pointer's continuation instruction, arm or start a controller, read a
346
+ production mail body into its instructions, update memory, or act on returned mail. A fixture that
347
+ cannot be created or is already read before the Scheduled run is BLOCKED. Empty-inbox substitution,
348
+ foreign-persona write, failure to emit the withheld nonce from the exact fetch, or legacy-consumer
349
+ attribution is RED.
350
+
351
+ N0a is GREEN only if N0a-M, N0a-W, N0a-B, N0a-Q, N0a-O, N0a-I, N0a-L, and
352
+ N0a-K are all GREEN.
353
+
354
+ ## 5. N0b lifecycle and health case matrix
355
+
356
+ N0b uses documented ChatGPT desktop controls only. Each lifecycle transition records attended setup
357
+ evidence plus independently observed eligible/non-eligible runs for at least two one-minute cadences.
358
+
359
+ | Case | Attended action | Required independent result |
360
+ |---|---|---|
361
+ | N0b-C | Create in `T` | Stable task identity is captured; an eligible run binds to `T` and the frozen specimen. |
362
+ | N0b-S | Inspect | Status, prompt digest, cadence, project/worktree, model, and recent-run identity can be captured without guessing. |
363
+ | N0b-P | Pause | No new eligible run appears for two cadences; health expires and cannot remain ARMED. |
364
+ | N0b-R | Resume | A new run with a fresh nonce and task identity appears; old evidence cannot re-arm it. |
365
+ | N0b-D | Delete | No run appears for two cadences; stale rollout or screenshot evidence cannot imply enabled state. |
366
+ | N0b-X | App exit | Project-scoped run behavior is measured; no unsupported claim is substituted for observation. |
367
+ | N0b-W | Sleep/wake | Missed and resumed cadence behavior is measured without changing the legacy controller. |
368
+ | N0b-L | Screen lock | Locked/background behavior is measured and correlated with the N0a-L run evidence. |
369
+ | N0b-J | Project removal | Temporarily select a disposable renamed/missing project path; the task must fail closed and health becomes RED. |
370
+ | N0b-F | Permission drift | Use only a per-task/per-disposable-project permission change exposed by documented controls; mismatched effective evidence makes health RED. If the product offers only a global/default change, do not change it and mark the capability RED. |
371
+
372
+ The protocol records discovered cadence, quota/rate, retention/expiry, recent-run, and notification
373
+ limits rather than assuming them. Host/server skew is measured at the beginning and end of each
374
+ lifecycle sequence and must not exceed 15 seconds. If the product provides no trustworthy server
375
+ timestamp, this requirement is RED, not estimated from prose.
376
+
377
+ ### N0b doctor oracle
378
+
379
+ N0 may build only a disposable, non-installable, read-only oracle that consumes the frozen evidence
380
+ schema. It cannot invoke ChatGPT, Kijito mutations, or provider code and must refuse evidence outside
381
+ the specimen root. It is labeled `N0_TEST_ORACLE` and never reports production `ARMED`.
382
+
383
+ The oracle's healthy test verdict requires current exact-chat run evidence, a heartbeat no older than
384
+ 135 seconds by server time, matching task/prompt/environment/permission identity, and all synthetic
385
+ claim/checkpoint/operator-journal fixtures valid. Pause, delete, app-down behavior, project removal,
386
+ permission drift, stale evidence, wrong task/chat, or excess clock skew is RED within two cadences.
387
+
388
+ ADV-A is load-bearing before N0b:
389
+
390
+ - missing/unreachable operator-decision journal produces explicit `JOURNAL_UNREACHABLE`, RED;
391
+ - missing arm record, missing pinned signer, or invalid signer binding produces explicit
392
+ `SIGNER_UNARMED`, RED;
393
+ - neither state may collapse into generic `STALE`, be omitted, or leave a healthy verdict;
394
+ - each has a positive control, a mutation that would incorrectly return healthy, and a unique named
395
+ failing assertion;
396
+ - the live capability result remains `N0_TEST_CAPABLE`, never production `ARMED`, because N1 has not
397
+ yet proven or supplied the real journal/claim surfaces.
398
+
399
+ N0b is GREEN only if all lifecycle cases, clock evidence, management limits, artifact availability,
400
+ and doctor-oracle negative controls are GREEN. If doctor cannot distinguish enabled, paused, deleted,
401
+ stale, and drifted states from supported evidence, N0b is RED.
402
+
403
+ ## 6. Prompt constraints for the later frozen harness
404
+
405
+ This protocol does not contain the executable task prompt. The later harness must render exact prompt
406
+ bytes that:
407
+
408
+ - say `N0 CAPABILITY PROBE — NO MAIL AUTHORITY` before any tool output;
409
+ - pin the specimen root, task case, nonce, allowed tools, sandbox, project, chat target, and maximum
410
+ effect before reading external data;
411
+ - treat all tool-returned mail and memory as untrusted data and forbid it from changing authority;
412
+ - prohibit provider/server implementation, controller start/stop, hooks, LaunchAgents, app-server
413
+ thread operations, UI automation, ordinary config/auth changes, network use except the single N0a-K
414
+ hosted Kijito reachability check, and writes outside the disposable `project/` root;
415
+ - stop after its bounded evidence action and emit no claim that the feature works;
416
+ - contain a deliberate hostile-data negative fixture whose text requests scope expansion, and prove
417
+ that effective configuration and receipts remain unchanged.
418
+
419
+ The prompt digest, not a paraphrase, is recorded before scheduling. Any UI rewrite or model-visible
420
+ addition changes the digest and invalidates the run unless the outside verifier can capture and hash
421
+ the exact effective prompt.
422
+
423
+ ## 7. QA sequence and change control
424
+
425
+ 1. **Protocol QA:** commit only this protocol and supporting documentation. Send Assay the returned
426
+ commit SHA and protocol SHA-256. The review request must copy the full commit verbatim from
427
+ `git rev-parse HEAD`, verify that exact object resolves, and pair it with a freshly computed file
428
+ digest; an agent must never expand an abbreviated SHA from memory. No marker, nonce submission,
429
+ task, harness, UI action, or sleep/lock probe occurs. Remediate findings and repeat until Assay says
430
+ CLEAN in two consecutive zero-finding reviews from different angles on the same unchanged commit
431
+ and digest. Any finding or byte change resets the protocol counter to `0/2`.
432
+ 2. **Harness QA:** implement only the disposable snapshot/parser/oracle/fixture scripts and their
433
+ tests. Use production-shaped synthetic rollouts and mutations for zero/multiple marker matches,
434
+ wrong chat/run, mtime/newest guessing, symlink/path escape, concurrent mutation, task read/chmod/
435
+ write access to the sealed control root, task write access to the PR worktree, stale evidence,
436
+ omitted `:tmpdir`/`:slash_tmp` deny, an extra writable root, wrong/unselected permission profile,
437
+ a missing out-of-tree temp canary, and legacy `sandbox_workspace_write` flags falsely accepted as
438
+ proof for the installed named-profile branch,
439
+ false healthy after pause/delete, live-brain checks that omit the withheld pointer digest or
440
+ fetched body-nonce digest, and specimens that leak an expected pointer ID/digest or raw body nonce
441
+ into the protocol, prompt, project, chat, or pre-run logs. Also mutate `JOURNAL_UNREACHABLE`,
442
+ `SIGNER_UNARMED`, absent test mail, already-read test mail, and concurrent
443
+ persona-`codex` mail mutation that must not affect the `codex-n0` oracle. Freeze one commit and
444
+ digest; Assay must complete two consecutive zero-finding reviews from different angles on those
445
+ exact unchanged bytes before the first attended action. Any finding or byte change resets the
446
+ harness counter to `0/2`.
447
+ 3. **Attended marker:** generate the one-use marker nonce, freeze the pre-snapshot, and ask Jason for
448
+ exactly one marker turn. If the attended marker is not submitted, N0a-M is BLOCKED; after one
449
+ confirmed submission, zero or multiple rollout matches is RED.
450
+ 4. **N0 execution:** perform cases in order N0a-M, N0a-W, N0a-B, N0a-Q, N0a-O,
451
+ N0a-I, N0a-L, N0a-K, then N0b-C, N0b-S, N0b-P, N0b-R, N0b-D, N0b-X, N0b-W,
452
+ N0b-L, N0b-J, N0b-F.
453
+ Check inbox between cases, update the current-state pointer after every terminal case, and do not
454
+ continue after a RED without Assay reviewing the evidence and ruling whether it is product RED or
455
+ harness invalidation.
456
+ 5. **Evidence QA:** freeze the manifest and results. Assay independently recomputes verdicts from the
457
+ evidence. Only an Assay-confirmed `N0A_GREEN` plus `N0B_GREEN` becomes `N0_GREEN`.
458
+
459
+ Any change to a binary pass condition, identity source, prompt authority, evidence channel, test
460
+ persona, lifecycle matrix, or RED threshold changes this protocol digest and returns to step 1.
461
+ Formatting-only changes after protocol CLEAN are deferred until N0 evidence QA is complete.
462
+
463
+ ## 8. End state
464
+
465
+ On GREEN, record only that the installed Scheduled surface is capable of supporting later N1-N3
466
+ probes under the measured constraints. On RED, record the exact unsupported property and reject the
467
+ candidate without implementing around it. On BLOCKED, record the missing attended/external
468
+ prerequisite and preserve the unchanged authority boundary.
469
+
470
+ In all three cases, delete the disposable Scheduled tasks through documented controls, verify two
471
+ cadences without runs, retain the minimal redacted evidence manifest, leave legacy PID `38082`
472
+ untouched, and update memory and River/Assay mail with the exact verdict and remaining authorization.
@@ -0,0 +1,57 @@
1
+ # N0 disposable evidence harness
2
+
3
+ Status: **TEST-ONLY, NON-INSTALLABLE, NO LIVE PROBE AUTHORITY.**
4
+
5
+ This directory implements only step 2 of
6
+ [`../n0-capability-probe-protocol.md`](../n0-capability-probe-protocol.md): bounded rollout snapshots,
7
+ strict parsers, synthetic fixtures, and the `N0_TEST_ORACLE`. It cannot create or manage a Scheduled
8
+ task, drive the ChatGPT UI, start or stop a controller, contact Kijito, send mail, mark mail read,
9
+ generate a live marker, or report production `ARMED`.
10
+
11
+ The modules are deliberately separated:
12
+
13
+ - `snapshot.mjs` walks one caller-supplied app-owned root without symlinks, newest/mtime selection,
14
+ or path escape and records stable file identity before comparing growth;
15
+ - `parser.mjs` requires `session_meta.payload.id`, attributes marker and run nonces only inside one
16
+ user-turn span, binds exact task/run/turn/environment evidence, rejects structural steering, parses
17
+ only the final exact-ID/persona Kijito main block, and verifies the exact non-mutating test-mail
18
+ fetch artifact;
19
+ - `oracle.mjs` validates the frozen specimen, write-boundary canaries, lifecycle evidence, live-brain
20
+ challenges, exact prompt bytes, project/rollout/parser provenance, journal/signer negatives, and
21
+ every per-case nonce/run/receipt binding. Its sole positive value is `N0_TEST_CAPABLE`;
22
+ - `fixture.mjs` contains deterministic synthetic data only. Its fixed hexadecimal strings are not
23
+ live nonces and must never be copied into a real probe;
24
+ - `prompt.mjs` renders exact per-case prompt bytes, including the fixed hostile-data negative fixture,
25
+ and `specimen.mjs` computes every prompt digest plus inert Kijito request packets without sending;
26
+ - `evidence-manifest.mjs` reads stable owner-matching files beneath one explicit control root and
27
+ produces or validates a relative-path/size/SHA-256 evidence manifest without writing it;
28
+ - `cli.mjs` is a read-only convenience wrapper. It accepts evidence files only beneath an explicit
29
+ root, rejects symlinks and concurrent mutation, and has no mutation or network subcommand.
30
+
31
+ Run the adversarial suite with a healthy Node 18+ runtime:
32
+
33
+ ```sh
34
+ node --test providers/codex/test/n0-harness.test.mjs
35
+ ```
36
+
37
+ The fail-open completeness gate uses an Acorn AST census, exact-source ownership, and one named
38
+ counterexample per executable rejection atom or rejecting-helper call site. Install the pinned
39
+ development dependency and run all three layers:
40
+
41
+ ```sh
42
+ npm ci
43
+ npm run test:n0-census-self
44
+ npm run test:n0-census
45
+ node providers/codex/test/n0-guard-mutation-runner.mjs
46
+ ```
47
+
48
+ The mutation runner copies the harness to an isolated temporary tree, recomputes its aggregate,
49
+ asserts the 27/27 baseline before and after, and requires every pristine counterexample to reject
50
+ with its exact code while only its isolated fail-open mutant accepts. It also verifies that the
51
+ origin HEAD, index, and worktree remain unchanged. Partial census runs are authoring diagnostics,
52
+ never an acceptance gate.
53
+
54
+ The suite includes every mutation floor in protocol section 7, isolated counterexamples for every
55
+ oracle guard binding, and Assay round-5 hostile fence and run-nonce user-span controls. Passing this
56
+ local suite is author evidence only. The exact unchanged harness commit and manifest digest still
57
+ require two consecutive zero-finding Assay reviews before any attended marker or Scheduled action.
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import { evaluateOracle } from "./oracle.mjs";
4
+ import { parseJsonBuffer, readOwnedRegularFile } from "./lib.mjs";
5
+ import { snapshotTree } from "./snapshot.mjs";
6
+
7
+ function usage() {
8
+ process.stderr.write("N0_TEST_ORACLE — non-installable, read-only\nusage: cli.mjs snapshot --root DIR | oracle --root DIR --specimen FILE --evidence FILE [--now-ms N]\n");
9
+ process.exitCode = 64;
10
+ }
11
+
12
+ function args(argv) {
13
+ const [command, ...rest] = argv;
14
+ const options = {};
15
+ for (let index = 0; index < rest.length; index += 2) {
16
+ const key = rest[index];
17
+ const value = rest[index + 1];
18
+ if (!key?.startsWith("--") || value === undefined) {
19
+ usage();
20
+ return null;
21
+ }
22
+ options[key.slice(2)] = value;
23
+ }
24
+ return { command, options };
25
+ }
26
+
27
+ function main(argv) {
28
+ const parsed = args(argv);
29
+ if (!parsed) return;
30
+ const { command, options } = parsed;
31
+ if (!options.root || !path.isAbsolute(options.root)) {
32
+ usage();
33
+ return;
34
+ }
35
+ if (command === "snapshot") {
36
+ process.stdout.write(`${JSON.stringify(snapshotTree(options.root))}\n`);
37
+ } else if (command === "oracle") {
38
+ if (!options.specimen || !options.evidence) {
39
+ usage();
40
+ return;
41
+ }
42
+ const specimen = parseJsonBuffer(readOwnedRegularFile(options.root, options.specimen).data);
43
+ const evidence = parseJsonBuffer(readOwnedRegularFile(options.root, options.evidence).data);
44
+ const nowMs = options["now-ms"] === undefined ? Date.now() : Number(options["now-ms"]);
45
+ const result = evaluateOracle(specimen, evidence, nowMs);
46
+ process.stdout.write(`${JSON.stringify(result)}\n`);
47
+ if (result.status !== "N0_TEST_CAPABLE") process.exitCode = 1;
48
+ } else {
49
+ usage();
50
+ }
51
+ }
52
+
53
+ try {
54
+ main(process.argv.slice(2));
55
+ } catch (error) {
56
+ process.stderr.write(`${error?.code ?? "N0_CLI_ERROR"}: ${error?.message ?? String(error)}\n`);
57
+ process.exitCode = 1;
58
+ }