@wacando/agent 0.0.0-stage → 2.0.0-rc.1

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 (117) hide show
  1. package/.agents/plugins/marketplace.json +10 -0
  2. package/.claude-plugin/marketplace.json +12 -0
  3. package/.cursor-plugin/marketplace.json +12 -0
  4. package/README.md +166 -2
  5. package/STATUS.md +121 -0
  6. package/THIRD-PARTY-NOTICES.txt +1928 -0
  7. package/adapters/README.md +12 -0
  8. package/adapters/claude/mcp.mjs +7 -0
  9. package/adapters/codex/mcp.mjs +7 -0
  10. package/adapters/cursor/mcp.mjs +7 -0
  11. package/bin/wacando-agent.mjs +243 -0
  12. package/docs/AGENT_TOOLS.md +129 -0
  13. package/docs/CURSOR_IDENTITY_RESUME.md +579 -0
  14. package/docs/MCP-AUDIT.md +355 -0
  15. package/docs/MCP-CONTRACT.md +138 -0
  16. package/docs/MIGRATION-2.0.md +36 -0
  17. package/docs/OMEMO_PREKEY_INTEGRATION.md +125 -0
  18. package/docs/ONBOARDING-VALIDATION.md +88 -0
  19. package/docs/ONBOARDING.md +212 -0
  20. package/docs/ONBOARDING_PLUGIN_CONTRACT.md +156 -0
  21. package/docs/PLUGIN_HANDOFF_FOLLOWUP.md +178 -0
  22. package/docs/PUBLIC_ONBOARDING_READINESS.md +156 -0
  23. package/docs/RELEASE.md +25 -0
  24. package/docs/WACANDO_PACKAGES.md +26 -0
  25. package/docs/evidence/issue-2/DOCUMENTARY_FOLLOWUP.md +107 -0
  26. package/docs/evidence/issue-2/QUALIFICATION_LINK_FOLLOWUP.md +105 -0
  27. package/docs/evidence/issue-2/REVIEW_FOLLOWUP.md +93 -0
  28. package/docs/evidence/issue-2/baseline.txt +76 -0
  29. package/docs/evidence/issue-2/check-mcp.txt +14 -0
  30. package/docs/evidence/issue-2/check.txt +376 -0
  31. package/docs/evidence/issue-2/codex-exec-help.json +11 -0
  32. package/docs/evidence/issue-2/codex-help.json +10 -0
  33. package/docs/evidence/issue-2/cursor-help.json +10 -0
  34. package/docs/evidence/issue-2/documentary-check.txt +376 -0
  35. package/docs/evidence/issue-2/documentary-validation.txt +7 -0
  36. package/docs/evidence/issue-2/local-build-api.txt +203 -0
  37. package/docs/evidence/issue-2/local-build-first.txt +87 -0
  38. package/docs/evidence/issue-2/local-build-policy-first.txt +34 -0
  39. package/docs/evidence/issue-2/local-build-policy.txt +153 -0
  40. package/docs/evidence/issue-2/local-build-web.txt +219 -0
  41. package/docs/evidence/issue-2/local-check-mcp.txt +14 -0
  42. package/docs/evidence/issue-2/local-check.txt +376 -0
  43. package/docs/evidence/issue-2/local-codex-probe.txt +3 -0
  44. package/docs/evidence/issue-2/local-codex-probe2.txt +3 -0
  45. package/docs/evidence/issue-2/local-codex.txt +2 -0
  46. package/docs/evidence/issue-2/local-cursor-probe.txt +1 -0
  47. package/docs/evidence/issue-2/local-cursor.txt +2 -0
  48. package/docs/evidence/issue-2/local-down.txt +32 -0
  49. package/docs/evidence/issue-2/local-health.txt +1 -0
  50. package/docs/evidence/issue-2/local-live.txt +28 -0
  51. package/docs/evidence/issue-2/local-seed.txt +26 -0
  52. package/docs/evidence/issue-2/local-status.txt +6 -0
  53. package/docs/evidence/issue-2/local-up.txt +18 -0
  54. package/docs/evidence/issue-2/prepare-local-stack.py +67 -0
  55. package/docs/evidence/issue-2/qualification-body-validation.txt +9 -0
  56. package/docs/evidence/issue-2/qualification-link-check.txt +376 -0
  57. package/docs/evidence/issue-2/qualification-link-validation.txt +20 -0
  58. package/docs/evidence/issue-2/review-check.txt +376 -0
  59. package/docs/evidence/issue-2/review-validation.txt +218 -0
  60. package/docs/evidence/issue-7/README.md +21 -0
  61. package/docs/evidence/issue-7/check-mcp.txt +9 -0
  62. package/docs/evidence/issue-7/check.txt +381 -0
  63. package/docs/evidence/issue-7/live.txt +23 -0
  64. package/docs/evidence/issue-8/README.md +56 -0
  65. package/docs/evidence/issue-8/check-mcp.txt +9 -0
  66. package/docs/evidence/issue-8/check.txt +465 -0
  67. package/docs/evidence/issue-8/e2e-desktop-observer-human-devices.txt +30 -0
  68. package/docs/evidence/issue-8/e2e-desktop-observer-main-without-109.txt +17 -0
  69. package/docs/evidence/issue-8/e2e-desktop-observer.txt +26 -0
  70. package/docs/evidence/issue-8/live-regression-human-devices.txt +24 -0
  71. package/docs/evidence/issue-8/live-regression.txt +23 -0
  72. package/docs/evidence/issue-8/review-fixes/README.md +30 -0
  73. package/docs/evidence/issue-8/review-fixes/check-mcp.txt +9 -0
  74. package/docs/evidence/issue-8/review-fixes/check.txt +484 -0
  75. package/docs/evidence/issue-8/review-fixes/e2e-desktop-observer.txt +28 -0
  76. package/docs/evidence/issue-8/review-fixes/onboarding-live.txt +15 -0
  77. package/docs/evidence/issue-8/review-fixes/session-replacement-live.txt +13 -0
  78. package/docs/evidence/npm-release/check-mcp.txt +9 -0
  79. package/docs/evidence/npm-release/check.txt +484 -0
  80. package/docs/evidence/npm-release/e2e-desktop-observer.txt +28 -0
  81. package/package.json +66 -4
  82. package/packages/core/limits.mjs +24 -0
  83. package/packages/core/store.mjs +92 -0
  84. package/packages/core/validation.mjs +25 -0
  85. package/packages/mcp/agent-tools.mjs +82 -0
  86. package/packages/mcp/server.mjs +161 -0
  87. package/packages/mcp/stdio.mjs +42 -0
  88. package/packages/onboarding/network.mjs +60 -0
  89. package/packages/onboarding/service.mjs +235 -0
  90. package/packages/onboarding/storage.mjs +99 -0
  91. package/packages/onboarding/version.mjs +2 -0
  92. package/packages/runtime/conversations.mjs +384 -0
  93. package/packages/runtime/device.mjs +34 -0
  94. package/packages/runtime/index.mjs +8 -0
  95. package/packages/runtime/pairing.mjs +110 -0
  96. package/packages/runtime/runtime.mjs +213 -0
  97. package/packages/runtime/session.mjs +52 -0
  98. package/packages/server/cli.mjs +18 -0
  99. package/packages/server/server.mjs +110 -0
  100. package/packages/server/stream.mjs +42 -0
  101. package/packages/web/app.js +85 -0
  102. package/packages/web/history.js +48 -0
  103. package/packages/web/index.html +17 -0
  104. package/packages/web/style.css +1 -0
  105. package/plugins/wacando/.codex-plugin/plugin.json +18 -0
  106. package/plugins/wacando/.mcp.json +8 -0
  107. package/plugins/wacando/skills/wacando/SKILL.md +33 -0
  108. package/plugins/wacando-claude/.claude-plugin/plugin.json +10 -0
  109. package/plugins/wacando-claude/.mcp.json +8 -0
  110. package/plugins/wacando-claude/README.md +88 -0
  111. package/plugins/wacando-claude/skills/open/SKILL.md +66 -0
  112. package/plugins/wacando-cursor/.cursor-plugin/plugin.json +11 -0
  113. package/plugins/wacando-cursor/README.md +48 -0
  114. package/plugins/wacando-cursor/commands/wacando-open.md +10 -0
  115. package/plugins/wacando-cursor/mcp.json +8 -0
  116. package/plugins/wacando-cursor/skills/wacando/SKILL.md +50 -0
  117. package/provenance.json +503 -0
@@ -0,0 +1,579 @@
1
+ # Cursor identity resume — issue 2
2
+
3
+ Status: shutdown regression and **issue #2 runtime acceptance checks PASS**.
4
+ King's explicit decision, relayed in mission
5
+ `1dc1a826-eefb-4610-bdfa-5027f88ab4d0`, accepts identity resume and revocation
6
+ proved by all three runtime profiles against real API/Postgres/ejabberd/OMEMO.
7
+ **Actual Cursor/Codex/Claude CLIs are NOT QUALIFIED in this sandbox**; their
8
+ qualification is tracked in [Wacando #17](https://github.com/lbframe/wacando/issues/17).
9
+ The recorded Cursor/Codex EROFS failures remain residual limitations, never PASS.
10
+ This decision changes the acceptance scope, not the observed test results.
11
+ Independent review is pending. No package was published and no lock was removed
12
+ in order to obtain a successful resume.
13
+
14
+ ## Demonstrated defect and scope
15
+
16
+ Base: `d0cf8b2` (`origin/main`). Linux, Node.js 22.22.0, npm 11.11.1.
17
+ Installed host versions observed: Cursor Agent CLI `2026.09.26-dd393fe`, Codex
18
+ CLI `0.162.1`. Neither host was used to claim an actual API/XMPP resume in this run.
19
+
20
+ The MCP server used to arm an unconditional `process.exit(0)` one second after
21
+ EOF/SIGTERM. `OnboardingService` releases a live session's exclusive `.lock` only
22
+ **after** `session.stop()` resolves. The checked-in `@xmpp/client` implementation
23
+ can wait two seconds closing the stream and another two closing the socket.
24
+ Thus normal shutdown can exit successfully before releasing its session lock.
25
+ The same deadline can interrupt saving an installation response after redemption.
26
+
27
+ The regression test holds session cleanup behind a controlled barrier for 1.2
28
+ seconds, crosses the old deadline, then allows the owner to release its own lock.
29
+ It generates real OMEMO local state, compares it byte for byte after cleanup, and
30
+ reacquires the released lock. A separate test holds an actual loopback HTTP
31
+ verification response until after the old deadline and checks it is saved before
32
+ exit. These are process/storage lifecycle tests. The session stop barrier does
33
+ not simulate or qualify the XMPP contract.
34
+
35
+ All three fail against an isolated archive of the base with exit code 1; both
36
+ session cases observe the child exiting with code 0 before cleanup. All pass in
37
+ the final full suite. This demonstrates a local shutdown defect consistent with
38
+ the production report, **not proof that it was the cause of that particular
39
+ Cursor host failure**. A held prior process, shutdown timing, or another host
40
+ issue must still be distinguished using the actual host checker and approved API.
41
+
42
+ ## Change
43
+
44
+ - Wait for onboarding cleanup and any currently dispatched call before applying
45
+ the final one-second stdout deadline. A separate 45-second cap exits with code
46
+ 1 if cleanup cannot finish. It never reaps or bypasses a lock.
47
+ - Wait for in-flight background recovery on close and avoid opening/publishing a
48
+ new session after close was requested. Keep the existing credentials, host/origin
49
+ partition, key paths and exclusive locking.
50
+ - Regenerate all three plugin runtimes with the build; generated files were not
51
+ edited directly.
52
+ - Bring the isolated host checker protections from the historical public fixture
53
+ into the normal loopback checker: isolated Cursor workspace alias, own process
54
+ group cleanup, parsed affirmative `verified: true` / `connected: true`, private
55
+ identity equality, and clear rejection after owner revocation. Cursor's outer
56
+ success/isError flag alone is insufficient. Raw host output is suppressed.
57
+
58
+ The checker fails if its own descendants cannot close within ten seconds. Any
59
+ forced termination is a failed qualification, never a successful retry with a
60
+ removed lock. The checker remains loopback-only; no public acceptance bypass was
61
+ added. Its event parser tests use explicit synthetic wrappers, not recorded host
62
+ payloads from a real acceptance run.
63
+
64
+ ## Original implementation commands and real outputs
65
+
66
+ The repository contains no separate lint/typecheck scripts. `npm run check`
67
+ builds, checks generated output and runs all Node tests. Full suites were bounded
68
+ by `timeout 600s` and run in the foreground.
69
+
70
+ Baseline reproduction, from the plugin checkout:
71
+
72
+ ```sh
73
+ baseline_dir=$(mktemp -d /tmp/wacando-issue2-before.XXXXXX)
74
+ git archive origin/main | tar -x -C "$baseline_dir"
75
+ cp tests/onboarding-shutdown.test.mjs "$baseline_dir/tests/onboarding-shutdown.test.mjs"
76
+ cd "$baseline_dir"
77
+ timeout 600s node --test tests/onboarding-shutdown.test.mjs
78
+ ```
79
+
80
+ Actual isolated directory: `/tmp/wacando-issue2-before.56NFHw`. Exit 1.
81
+ [Complete baseline output](evidence/issue-2/baseline.txt):
82
+
83
+ ```text
84
+ # tests 3
85
+ # suites 0
86
+ # pass 0
87
+ # fail 3
88
+ # cancelled 0
89
+ # skipped 0
90
+ ```
91
+
92
+ Original full check before the approved API target, from the plugin checkout:
93
+
94
+ ```sh
95
+ npm_config_cache=/tmp/wacando-issue2-npm-cache timeout 600s npm run check
96
+ ```
97
+
98
+ Exit 0. [Complete output](evidence/issue-2/check.txt):
99
+
100
+ ```text
101
+ # tests 57
102
+ # suites 0
103
+ # pass 54
104
+ # fail 0
105
+ # cancelled 0
106
+ # skipped 3
107
+ # todo 0
108
+ ```
109
+
110
+ This includes Cursor/Codex cleanup and key preservation, in-flight verification
111
+ and background recovery cleanup, parsed result validation, and both packaged
112
+ MCP runtimes rejecting a locally revoked identity while still answering `ping`
113
+ and exiting with code 0. Existing injected control-plane rejection tests cover
114
+ HTTP 403/AGENT_REVOKED, but are not a real remote revocation check.
115
+
116
+ The first check attempt used the default npm cache and failed the packaging test:
117
+ `EROFS` opening `/home/kyle/.npm/_cacache/tmp/da3aeb1a`. No application test failure
118
+ was inferred from that sandbox restriction; the final run used the writable
119
+ cache above. An intermediate new background-recovery test also failed because
120
+ its simulated scheduler retained an already-executed timer in the test's Set;
121
+ the fixture now consumes it before calling the callback. The final output is
122
+ from the corrected test, with no retry of a live identity.
123
+
124
+ ```sh
125
+ npm_config_cache=/tmp/wacando-issue2-npm-cache timeout 600s npm run check:mcp
126
+ ```
127
+
128
+ Exit 0. [Complete output](evidence/issue-2/check-mcp.txt):
129
+
130
+ ```text
131
+ Official Inspector 2.7.0 passed: wacando
132
+ Official Inspector 2.7.0 passed: wacando-cursor
133
+ Official Inspector 2.7.0 passed: wacando-claude
134
+ ```
135
+
136
+ ```sh
137
+ npm run build
138
+ npm run check:generated
139
+ git diff --check
140
+ ```
141
+
142
+ Exit 0 for each. Real output:
143
+
144
+ ```text
145
+ Built self-contained codex plugin: wacando@1.0.0
146
+ Built self-contained cursor plugin: wacando-cursor@1.0.0
147
+ Built self-contained claude plugin: wacando-claude@1.0.0
148
+ Verified exact runtime: wacando
149
+ Verified exact runtime: wacando-cursor
150
+ Verified exact runtime: wacando-claude
151
+ ```
152
+
153
+ `git diff --check` produced no output for the then-uncommitted changes.
154
+ This historical working-tree check does not cover the cumulative PR diff;
155
+ see the whitespace scope correction in the documentary follow-up evidence.
156
+
157
+ ## Approved local qualification — October 15, 2026
158
+
159
+ King authorized an ephemeral local development target, relayed by
160
+ `wacando.lead`. This run used Wacando `origin/main` at
161
+ `66c754756e83e8685d61e1c0312b690d77b3bdd3`, in the detached checkout
162
+ `/home/kyle/timon-projects/wacando/agent2-api`. No service source, frozen document,
163
+ production/public service, account credential or issue-10 service was changed.
164
+ The existing plugin branch and shutdown-lock regressions were retained.
165
+
166
+ The target derives from `docker-compose.dev.yml`, uses the repository's API,
167
+ policy and web Dockerfiles, and injects the official `ejabberd.ci.yml` and dev
168
+ seed. The seed's container selector was restricted to this project's service;
169
+ the original ancestor-image selector could otherwise select issue 10.
170
+
171
+ | Service | Loopback host port | Container port |
172
+ | --- | --- | --- |
173
+ | Postgres | 25432 | 5432 |
174
+ | ejabberd c2s / WebSocket / component | 25222 / 25280 / 25347 | 5222 / 5280 / 5347 |
175
+ | API / policy / web | 23001 / 23002 / 23000 | 3001 / 3002 / 3000 |
176
+
177
+ Project, network and volumes use `wacando-agent2`. All bindings use 127.0.0.1;
178
+ host port 3000 remains reserved for Timon. Only inherited development secrets
179
+ were used. Generated compose credentials remain outside evidence in a mode-0600
180
+ temporary file and were removed after shutdown.
181
+
182
+ **Retirement dependency:** the controllers create synthetic owners through
183
+ `/api/auth/sign-up/email`, then use the existing v1 onboarding endpoints. The
184
+ CLI checker imports `apps/web/src/lib/onboarding-prompts.ts`. This qualification
185
+ depends on the web onboarding flow scheduled for retirement in Wacando #42; it
186
+ does not qualify the future app/pairing flow. The web endpoint was checked by
187
+ HTTP only, not by a browser onboarding session.
188
+
189
+ ### Setup commands and observed results
190
+
191
+ From the main-service checkout:
192
+
193
+ ```sh
194
+ git fetch origin main
195
+ git worktree add --detach ../agent2-api origin/main
196
+ ```
197
+
198
+ From the plugin checkout, the retained
199
+ [fixture preparation script](evidence/issue-2/prepare-local-stack.py) reproduces
200
+ the exact generated compose and seed adaptation (both SHA-256 hashes matched
201
+ the initially generated files):
202
+
203
+ ```sh
204
+ python3 docs/evidence/issue-2/prepare-local-stack.py /home/kyle/timon-projects/wacando/agent2-api
205
+ timeout 120s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json up -d postgres ejabberd
206
+ timeout 180s bash /tmp/wacando-agent2-ci-up.sh
207
+ ```
208
+
209
+ Exit 0. [Seed output](evidence/issue-2/local-seed.txt) confirms real ejabberd
210
+ 26.7.0, the six documented synthetic dev accounts and all three shifted ports.
211
+
212
+ From the isolated main checkout, final successful image builds:
213
+
214
+ ```sh
215
+ BUILDX_CONFIG=/tmp/wacando-agent2-buildx timeout 600s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json build api
216
+ BUILDX_CONFIG=/tmp/wacando-agent2-buildx timeout 600s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json build policy
217
+ BUILDX_CONFIG=/tmp/wacando-agent2-buildx timeout 600s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json build web
218
+ timeout 120s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json up -d --no-deps --no-build api
219
+ timeout 120s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json up -d --no-build policy
220
+ timeout 120s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json up -d --no-build web
221
+ ```
222
+
223
+ Exit 0 for the final builds and starts. Complete logs:
224
+ [API](evidence/issue-2/local-build-api.txt),
225
+ [policy](evidence/issue-2/local-build-policy.txt),
226
+ [web](evidence/issue-2/local-build-web.txt),
227
+ [full-stack start](evidence/issue-2/local-up.txt).
228
+ API/contracts/XMPP and policy typechecks passed inside their images; the web's
229
+ Next build and TypeScript check passed. All five services were healthy in the
230
+ [status output](evidence/issue-2/local-status.txt).
231
+
232
+ ```sh
233
+ curl -fsS --max-time 5 http://localhost:23001/health
234
+ curl -fsS --max-time 5 http://localhost:23000/onboarding -o /dev/null -w 'Web onboarding HTTP %{http_code}\n'
235
+ ```
236
+
237
+ Exit 0: `{"ok":true}` and `Web onboarding HTTP 200`.
238
+ [Actual outputs](evidence/issue-2/local-health.txt).
239
+
240
+ Setup failures were infrastructure/dependency failures, not successful acceptance:
241
+ the default buildx cache was read-only; the first group build and first policy
242
+ build failed with `frontend grpc server closed unexpectedly`
243
+ ([group](evidence/issue-2/local-build-first.txt),
244
+ [policy](evidence/issue-2/local-build-policy-first.txt)). An exploratory legacy
245
+ API build reached `corepack prepare` but hit its 180-second timeout; a bounded
246
+ `docker pull node:24-alpine` then succeeded. The host briefly showed load 52.65
247
+ and full I/O pressure averaging 62.33% over 60 seconds. Retrying serial builds
248
+ with the writable buildx cache succeeded. No daemon or shared service was
249
+ restarted. A separate source dependency preparation completed with:
250
+
251
+ ```sh
252
+ COREPACK_HOME=/tmp/wacando-agent2-corepack timeout 600s pnpm install --frozen-lockfile --store-dir /tmp/wacando-agent2-pnpm-store
253
+ ```
254
+
255
+ The first default Corepack attempt failed on its read-only home cache. The final
256
+ install exited 0 with the frozen lockfile unchanged; API execution used the
257
+ Docker image, not these host dependencies.
258
+
259
+ ### Actual API/runtime checks
260
+
261
+ From the plugin checkout:
262
+
263
+ ```sh
264
+ npm_config_cache=/tmp/wacando-issue2-npm-cache timeout 600s npm ci --include=dev
265
+ WACANDO_LIVE_TEST_ORIGIN=http://localhost:23001 timeout 600s node --test tests/onboarding-live.test.mjs
266
+ npm_config_cache=/tmp/wacando-issue2-npm-cache WACANDO_LIVE_TEST_ORIGIN=http://localhost:23001 timeout 600s npm run check
267
+ npm_config_cache=/tmp/wacando-issue2-npm-cache timeout 600s npm run check:mcp
268
+ node --check scripts/check-host-onboarding.mjs
269
+ git diff --check
270
+ ```
271
+
272
+ All exit 0 at that stage (`git diff --check` checked then-uncommitted changes,
273
+ not the cumulative PR diff containing preserved raw logs). The initial checker
274
+ could not import `esbuild`; `npm ci` without
275
+ `--include=dev` inherited production dependency selection and did not install
276
+ it. The explicit dev install added two packages and resolved that prerequisite.
277
+
278
+ - [Standalone live suite](evidence/issue-2/local-live.txt): **3 passed, 0 failed,
279
+ 0 skipped**, with the installed Codex/Cursor/Claude runtimes and real
280
+ API/Postgres/ejabberd/OMEMO. It exercises official verification, enrollment,
281
+ public-bundle publication, a fresh MCP process resuming with consumed token,
282
+ byte-stable private identity, owner remote revocation and explicit rejection.
283
+ - [Full final check](evidence/issue-2/local-check.txt): **57 passed, 0 failed,
284
+ 0 skipped**, including those three real integration cases and the existing
285
+ shutdown-lock regressions. Generated runtimes match shared sources. No
286
+ separate plugin lint/typecheck scripts exist.
287
+ - [MCP Inspector](evidence/issue-2/local-check-mcp.txt): official Inspector 2.7.0
288
+ passes all three hosts. No MCP wire or generated-runtime change was needed in
289
+ this follow-up; the checker itself is a development controller.
290
+
291
+ The direct runtime suite requires actual parsed `connected: true` and proves
292
+ issue #2's agreed runtime acceptance: identity resume and owner revocation in
293
+ all three profiles. It does not qualify any real CLI event stream or host UI;
294
+ that separate qualification belongs to Wacando #17. It does not mock the XMPP
295
+ contract or delete a lock.
296
+
297
+ ### Actual CLI attempts and residual sandbox limitation
298
+
299
+ ```sh
300
+ WACANDO_CHECKOUT=/home/kyle/timon-projects/wacando/agent2-api WACANDO_LIVE_TEST_ORIGIN=http://localhost:23001 WACANDO_TEST_HOST=cursor timeout 600s node scripts/check-host-onboarding.mjs
301
+ WACANDO_CHECKOUT=/home/kyle/timon-projects/wacando/agent2-api WACANDO_LIVE_TEST_ORIGIN=http://localhost:23001 WACANDO_TEST_HOST=codex timeout 600s node scripts/check-host-onboarding.mjs
302
+ ```
303
+
304
+ Both exit 1, **before any confirmed MCP call**. Outputs:
305
+ [Cursor](evidence/issue-2/local-cursor.txt),
306
+ [Codex](evidence/issue-2/local-codex.txt). Each reports `Host exit=1`, `locks=none`,
307
+ `calls=0`, with raw host output suppressed. No `verified: true` or
308
+ `connected: true` host payload was obtained; host enrollment, resume and remote
309
+ revocation stages were therefore not reached.
310
+
311
+ The initial Cursor attempt failed at `mcp enable`: the runner exposes a
312
+ read-only `/tmp/.git`, so Cursor resolved the ancestor as its project root and
313
+ missed the fixture's `.cursor/mcp.json`. The checker now creates its own Git
314
+ root, directs Cursor project data and compile cache into the fixture, and
315
+ disables Cursor debug logs. This fixes configuration lookup without editing
316
+ global host configuration or changing home/credential locations. The next
317
+ attempt passed enable and reached the CLI, which exited before tool invocation.
318
+
319
+ Minimal probes containing no onboarding secrets isolated the remaining errors:
320
+
321
+ ```sh
322
+ git init --quiet /tmp/wacando-cursor-config-qGCIeU
323
+ CURSOR_DATA_DIR=/tmp/wacando-cursor-config-qGCIeU/cursor-data NODE_COMPILE_CACHE=/tmp/wacando-cursor-config-qGCIeU/cursor-cache CURSOR_AGENT_DISABLE_DEBUG_LOG=1 timeout 180s cursor-agent --print --output-format stream-json --auto-review --trust --workspace /tmp/wacando-cursor-config-qGCIeU 'Do not use any tools. Return only ready.'
324
+ timeout 180s codex exec --ephemeral --json --skip-git-repo-check --approve-for-me -C /tmp 'Do not use any tools. Return only ready.'
325
+ timeout 180s codex exec --ephemeral --json --skip-git-repo-check --approve-for-me -c 'sqlite_home="/tmp/wacando-agent2-codex-sqlite"' -c 'log_dir="/tmp/wacando-agent2-codex-logs"' -C /tmp 'Do not use any tools. Return only ready.'
326
+ ```
327
+
328
+ Each exits 1. Cursor's [actual diagnostic](evidence/issue-2/local-cursor-probe.txt):
329
+
330
+ ```text
331
+ Error: EROFS: read-only file system, open '/home/kyle/.cursor/sandbox-policies/sandbox-policy-ab97f73e2a601171'
332
+ ```
333
+
334
+ Codex's [initial](evidence/issue-2/local-codex-probe.txt) and
335
+ [isolated SQLite/log](evidence/issue-2/local-codex-probe2.txt) diagnostics:
336
+
337
+ ```text
338
+ Error: failed to initialize in-process app-server client: Read-only file system (os error 30)
339
+ ```
340
+
341
+ The launched mission's filesystem policy permits writes only in the project
342
+ and temporary roots; required host state locations are read-only. No policy
343
+ was disabled, no permissions widened, no home variable changed and no host
344
+ credentials copied or modified. Cursor/Codex CLI execution could not reach
345
+ MCP in this sandbox. No actual Claude CLI onboarding attempt is recorded here;
346
+ its passing runtime profile is not CLI evidence. All three CLIs remain
347
+ **NOT QUALIFIED in this sandbox**, with follow-up in Wacando #17 rather than
348
+ an additional acceptance blocker for issue #2. This documentary follow-up
349
+ requests no sandbox, HOME or credential access and changes no host installation
350
+ or configuration.
351
+
352
+ ### Cleanup and residual risks
353
+
354
+ ```sh
355
+ timeout 120s docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json down -v
356
+ docker compose -p wacando-agent2 -f /tmp/wacando-agent2.compose.json ps
357
+ docker volume ls --filter label=com.docker.compose.project=wacando-agent2
358
+ docker inspect wacando-issue10-postgres wacando-issue10-ejabberd --format '{{.Name}} status={{.State.Status}} started={{.State.StartedAt}} restarts={{.RestartCount}}'
359
+ ```
360
+
361
+ Exit 0. [Complete cleanup output](evidence/issue-2/local-down.txt) confirms all
362
+ five containers, the network and three named volumes removed, with no project
363
+ containers/volumes remaining. Issue-10 services were still running with zero
364
+ restarts and start times before this qualification. Synthetic identities,
365
+ owner sessions and server-side metadata disappeared with the isolated volumes;
366
+ fixture plugin state was cleaned normally. No lock was removed to manufacture
367
+ a success or retry the same identity.
368
+
369
+ - **HIGH, residual CLI qualification:** actual Cursor/Codex/Claude CLI
370
+ verification, identity resume after host restart, preserved keys and remote
371
+ revocation are **NOT QUALIFIED in this sandbox**. Cursor/Codex attempts failed
372
+ before MCP because of read-only host state; Claude CLI has no recorded attempt.
373
+ Host event-shape compatibility remains unproven. Wacando #17 owns this work;
374
+ the runtime PASS applies only to the agreed issue #2 acceptance scope.
375
+ - **MEDIUM, residual:** SIGKILL, a short host termination deadline or the
376
+ 45-second cleanup cap may leave stale locks. The fix does not recover crash
377
+ locks; existing locks stay preserved.
378
+ - Cursor IDE/Desktop, Codex Desktop, Claude host UI, macOS/Windows, encrypted
379
+ conversation exchange, future pairing/app flow and production were not tested.
380
+
381
+ Implementation and issue #2 runtime acceptance checks: **PASS** (three profiles,
382
+ real Postgres/ejabberd/OMEMO; identity resume and revocation).
383
+ Actual CLI qualification: **NOT QUALIFIED in this sandbox**, tracked in Wacando #17.
384
+ Independent review: **PENDING**. [PR #9](https://github.com/lbframe/wacando-agent/pull/9)
385
+ remains draft; issue #2 stays open until review/merge. No merge or publication.
386
+ The [Wacando #42](https://github.com/lbframe/wacando/issues/42) retirement dependency
387
+ remains: this evidence uses v1 web onboarding and its prompt builder, and does
388
+ not qualify the replacement app/pairing flow.
389
+
390
+ The [documentary follow-up evidence](evidence/issue-2/DOCUMENTARY_FOLLOWUP.md)
391
+ records the scope decision and checks separately from the unchanged raw logs.
392
+
393
+ ## Manual CLI qualification after host restart
394
+
395
+ **Procedure only, NOT EXECUTED by this documentary follow-up.** This is the
396
+ reproducible follow-up for [Wacando #17](https://github.com/lbframe/wacando/issues/17),
397
+ not additional evidence of a CLI PASS. Commands below are for an operator's
398
+ terminal on an already configured, authenticated host. The previous assertion
399
+ that all three CLIs' `--help` checks passed has no archived output and is
400
+ withdrawn, including the Claude claim. This follow-up separately archives
401
+ [Codex help](evidence/issue-2/codex-help.json),
402
+ [Codex exec help](evidence/issue-2/codex-exec-help.json) and
403
+ [Cursor help](evidence/issue-2/cursor-help.json), each exit 0; only their flags
404
+ are checked. No `claude --help` was run by this follow-up; Claude flags remain
405
+ unverified. Before using a launch command, verify its flags on that target
406
+ host; unavailable or incompatible commands leave that CLI NOT QUALIFIED.
407
+ Help success does not qualify MCP behavior.
408
+
409
+ The [original reviewed procedure snapshot](https://github.com/lbframe/wacando-agent/blob/abd3e52a285808df5b4cc3e1f53621421ef9b316/docs/CURSOR_IDENTITY_RESUME.md#manual-cli-qualification-after-host-restart)
410
+ is immutable and survives branch deletion. That snapshot predates the revocation
411
+ wording correction below; use the corrected acceptance in this document.
412
+
413
+ ### Preconditions and baseline, separately for each CLI
414
+
415
+ Use a designated test identity and an approved service with real Postgres,
416
+ ejabberd and OMEMO. Record the service revision, plugin revision and CLI version.
417
+ The installed package must include PR #9's cleanup fix. Its existing MCP
418
+ configuration must use the exact same API origin and protected state directory
419
+ before and after restart. Keep each host's own installation/agent and origin/host
420
+ partition; do not transfer state between Cursor, Codex and Claude.
421
+ `qualification_origin` must equal `new URL(configuredApiUrl).origin` exactly,
422
+ including normalized scheme, hostname and port, with no trailing slash or path
423
+ (for example, `http://localhost:23001`). The service stores state under the hash
424
+ of `URL.origin + "\n" + host`; the fingerprint function hashes the supplied
425
+ string literally. A trailing slash selects a different partition and fails
426
+ closed (typically ENOENT); it is not an identity change or a PASS.
427
+
428
+ The owner first completes the official installation/enrollment flow for that
429
+ CLI: observe the actual MCP payloads `verified: true` and `connected: true`, plus
430
+ owner-visible verification/connection. A host's prose, exit code, or outer
431
+ `isError: false` is insufficient. This procedure starts with that already
432
+ enrolled identity; it installs nothing and changes no host configuration.
433
+
434
+ Replace the angle-bracket placeholders below with local paths and public IDs
435
+ from that identity's metadata. They are not authentication credentials. Use the
436
+ existing protected state directory; do not change HOME or read/copy any durable
437
+ credential. Use a trusted existing workspace. For Cursor/Claude, the package
438
+ paths are the existing installed plugin directories, not marketplace roots.
439
+
440
+ ```sh
441
+ qualification_host='cursor' # Run separately with cursor, codex, then claude.
442
+ qualification_workspace='<absolute existing trusted workspace>'
443
+ qualification_origin='<exact URL.origin of configured API; no trailing slash>'
444
+ qualification_agent_id='<public agent ID>'
445
+ qualification_handle='<public @username/agent handle>'
446
+ qualification_installation_id='<public installation ID>'
447
+ qualification_state='<absolute existing protected state directory>'
448
+ cursor_package='<absolute installed wacando-cursor directory>'
449
+ claude_package='<absolute installed wacando-claude directory>'
450
+
451
+ codex --version
452
+ cursor-agent --version
453
+ claude --version
454
+ ```
455
+
456
+ The following local operator function reads only the selected OMEMO state and
457
+ emits a SHA-256 fingerprint of `identityKeyPair`, never the private key material.
458
+ The baseline remains local. Do not attach state files or raw CLI transcripts to
459
+ issues. After normal closure of the initial enrolled host, check its processes
460
+ have exited; the command also refuses a remaining lock. Do not delete a lock or
461
+ regenerate keys to pass this procedure.
462
+
463
+ ```sh
464
+ qualification_identity() {
465
+ node --input-type=module - "$qualification_state" "$qualification_origin" "$qualification_host" "$qualification_agent_id" <<'NODE'
466
+ import { readFile, readdir } from 'node:fs/promises';
467
+ import { createHash } from 'node:crypto';
468
+ import { join } from 'node:path';
469
+ const [root, origin, host, agentId] = process.argv.slice(2);
470
+ const hash = value => createHash('sha256').update(value).digest('hex');
471
+ const partition = join(root, hash(`${origin}\n${host}`));
472
+ if ((await readdir(partition)).some(name => name.endsWith('.lock'))) {
473
+ throw new Error('Host cleanup left a lock; qualification failed. Preserve state.');
474
+ }
475
+ const state = JSON.parse(await readFile(join(partition, `omemo-${hash(agentId)}`, 'classic-state.json'), 'utf8'));
476
+ if (!state.identityKeyPair) throw new Error('Missing identity; qualification failed.');
477
+ console.log(hash(JSON.stringify(state.identityKeyPair)));
478
+ NODE
479
+ }
480
+ qualification_evidence="$qualification_workspace/.wacando-qualification-$qualification_host"
481
+ (umask 077; mkdir "$qualification_evidence")
482
+ qualification_identity > "$qualification_evidence/identity-before.sha256"
483
+ ```
484
+
485
+ Require exit 0 at every step; stop if baseline creation fails. Keep the baseline
486
+ directory across any machine reboot. The fingerprint is for local comparison;
487
+ record only the comparison result in shared evidence.
488
+
489
+ ### Start a fresh host and resume, per CLI
490
+
491
+ Quit the initial CLI normally and wait for its MCP process to finish cleanup.
492
+ Start a new CLI process, not a resumed chat. For a machine-reboot run, shut down
493
+ normally, reboot through the host's usual controls, restore service availability,
494
+ and repeat the variable/function definitions above with exactly the same values;
495
+ keep the existing baseline directory and do not rerun `mkdir` or overwrite it.
496
+ Record which restart was exercised (CLI process or machine). No reboot was
497
+ performed in the sandbox evidence.
498
+
499
+ Build the same public-ID-only prompt before each launch:
500
+
501
+ ```sh
502
+ resume_prompt="Invoke the installed Wacando MCP tool wacando_enroll_agent exactly once with {\"apiBaseUrl\":\"$qualification_origin\",\"agentId\":\"$qualification_agent_id\",\"handle\":\"$qualification_handle\",\"installationId\":\"$qualification_installation_id\",\"enrollmentToken\":\"already-consumed\"}. Return its actual safe tool payload. Use no shell, HTTP or transport substitute. Do not inspect credentials or record this prompt. If the tool is unavailable or fails, report the failure and stop."
503
+ ```
504
+
505
+ Run only the command for the selected host, with its normal MCP approvals:
506
+
507
+ | CLI | Exact launch command | Required observation |
508
+ | --- | --- | --- |
509
+ | Cursor | `cursor-agent --workspace "$qualification_workspace" --plugin-dir "$cursor_package" "$resume_prompt"` | Actual `wacando_enroll_agent` tool payload has `connected: true` and the same handle; expand the tool result instead of trusting Cursor's outer success wrapper. |
510
+ | Codex | `codex -C "$qualification_workspace" "$resume_prompt"` | The already activated Wacando MCP server returns `connected: true` and the same handle in the actual tool payload. |
511
+ | Claude Code | From `cd "$qualification_workspace"`, run `claude --plugin-dir "$claude_package" "$resume_prompt"` | Actual Wacando tool payload has `connected: true` and the same handle. The automated Codex/Cursor event parser does not support Claude; inspect its actual MCP tool result manually. |
512
+
513
+ Keep the host open long enough to observe the tool result and the owner-visible
514
+ connection. `already-consumed` is a deliberately unusable placeholder: successful
515
+ resume must use the saved durable identity without a fresh token, installation,
516
+ agent or keypair. Then quit normally, wait for this host's MCP process to exit,
517
+ and run:
518
+
519
+ ```sh
520
+ qualification_identity > "$qualification_evidence/identity-after.sha256"
521
+ cmp "$qualification_evidence/identity-before.sha256" "$qualification_evidence/identity-after.sha256"
522
+ ```
523
+
524
+ Expected: both commands exit 0, `cmp` is silent, and no lock remains. Any missing
525
+ payload, timeout, forced termination, changed fingerprint or remaining lock is
526
+ FAIL for this CLI; preserve state and report the result without a retry that
527
+ removes a lock. Runtime PASS cannot turn that CLI result into PASS.
528
+
529
+ ### Owner revocation and fresh-host rejection, per CLI
530
+
531
+ Using the official owner UI, revoke this same designated test agent and confirm
532
+ its revoked status. Start another fresh CLI with the same launch command and
533
+ `resume_prompt`. For this already-saved identity, not yet marked revoked
534
+ locally, `enroll()` skips token redemption and calls `open()`. The API's
535
+ 403/`AGENT_REVOKED` rejection of durable access is handled there. Expected exact
536
+ MCP error text:
537
+
538
+ ```text
539
+ Agent revoked or durable access rejected. Connection attempts have stopped. Check the identity on the site.
540
+ ```
541
+
542
+ If background recovery has already persisted `revoked: true` before the call,
543
+ `enroll()` instead rejects with:
544
+
545
+ ```text
546
+ Agent revoked. Connection attempts have stopped. Create a new identity on the site.
547
+ ```
548
+
549
+ The shorter `Agent revoked. Connection attempts have stopped.` belongs to
550
+ initial token redemption when no agent is saved; it is not the expected path
551
+ for this procedure. These paths are in `packages/onboarding/service.mjs`
552
+ (`open()` catch, saved-revocation guard, and initial-enrollment catch).
553
+
554
+ Require exactly one actual `wacando_enroll_agent` result with `isError: true`,
555
+ rejection text matching the prefix `/^Agent revoked[. ]/`, and no
556
+ `connected: true` in that result. This accepts the relevant variants, consistent
557
+ with the live test's `/Agent revoked/` assertion and the stricter host parser's
558
+ prefix check. Host prose, an outer success/error wrapper, a missing tool result,
559
+ a generic error or a timeout cannot pass. The durable-access message also covers
560
+ other 401/403 rejections: the message alone does not prove owner revocation.
561
+ Require the earlier successful resume of the same identity and confirmed revoked
562
+ status in the official owner UI as well. The host must remain responsive.
563
+
564
+ Quit normally, then repeat the fingerprint comparison above: exit 0 and
565
+ identical identity, with no leftover lock. Revocation is the final step; do not
566
+ reuse that identity for a successful-resume scenario.
567
+
568
+ For each CLI, record the version, revisions, restart type, safe MCP result,
569
+ owner-visible status, normal shutdown, fingerprint equality, revocation result,
570
+ commands, exit codes, untested points, residual risks and PASS/FAIL. Never retain
571
+ temporary prompt tokens, cookies, credentials, private keys or message content.
572
+ Cursor/Codex EROFS before MCP means **NOT QUALIFIED**, never PASS; an unattempted
573
+ Claude CLI also stays **NOT QUALIFIED**. All three are currently **CLI not
574
+ qualified in the sandbox**.
575
+
576
+ **Wacando #42 dependency:** this v1 procedure's initial setup and historical
577
+ checker depend on the web onboarding flow and `onboarding-prompts.ts`. When
578
+ that flow is retired under [#42](https://github.com/lbframe/wacando/issues/42),
579
+ adapt initial setup to the approved replacement app/pairing flow for #17. Do not claim that this v1 evidence proves the future flow.