@bongos/core 1.19.1072 → 1.19.1074

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 (297) hide show
  1. package/.bongos-core.json +320 -465
  2. package/.claude/skills/feedback/SKILL.md +2 -2
  3. package/clients/bongos-client/README.md +1 -1
  4. package/clients/bongos-client/bongos-client.global.js +4 -60
  5. package/clients/bongos-client/index.cjs +4 -60
  6. package/clients/bongos-client/index.d.ts +5 -90
  7. package/clients/bongos-client/index.mjs +4 -60
  8. package/config/branding.neutral.json +1 -5
  9. package/config/modules.neutral.json +2 -3
  10. package/config/scheduled-routines.json +0 -2
  11. package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +1 -0
  12. package/docs/adr/0285-a-shared-box-holds-about-twelve-projects-per-gb-and-memory-is-the-wall.md +1 -0
  13. package/docs/adr/0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md +1 -1
  14. package/docs/adr/0327-a-cloud-host-runs-on-the-owners-account-and-the-key-is-borrowed.md +1 -1
  15. package/docs/adr/0341-the-page-is-the-unit-of-tweak-mode.md +6 -2
  16. package/docs/adr/0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md +94 -0
  17. package/docs/adr/README.md +2 -1
  18. package/docs/api/openapi.json +171 -1661
  19. package/docs/api-reference.md +9 -41
  20. package/docs/architecture.md +16 -1
  21. package/docs/branding-contract.md +0 -3
  22. package/docs/copy-inventory.md +454 -479
  23. package/docs/copy-registry.json +746 -978
  24. package/docs/file-map.md +5 -34
  25. package/docs/module-api-changelog.md +4 -0
  26. package/docs/modules-contract.md +13 -1
  27. package/docs/onboarding/diagrams/04-architecture.mmd +11 -22
  28. package/docs/onboarding/diagrams/README.md +3 -3
  29. package/docs/onboarding/diagrams/assertions.json +2 -22
  30. package/docs/onboarding/primer.md +9 -28
  31. package/docs/page-inventory.json +4 -1
  32. package/docs/page-readings.json +1032 -1077
  33. package/docs/recipes/render-pilot.md +29 -10
  34. package/migrations/core_257_module_store_registry.sql +87 -0
  35. package/migrations/core_258_drop_dev_box_tables.sql +55 -0
  36. package/modules/agents/module.json +1 -0
  37. package/modules/autonomy/module.json +1 -0
  38. package/modules/builder-settings/module.json +1 -0
  39. package/modules/builder-settings/render-prefs.js +4 -5
  40. package/modules/copy-desk/docx.js +78 -5
  41. package/modules/copy-desk/module.json +1 -0
  42. package/modules/copy-desk/routes/copy-desk.js +9 -9
  43. package/modules/copy-desk/tests/copy_docx.mjs +107 -1
  44. package/modules/copy-desk/tests/fixtures/word-docx.mjs +30 -0
  45. package/modules/discord/module.json +1 -0
  46. package/modules/economy/module.json +1 -0
  47. package/modules/economy/routes/credits.js +1 -2
  48. package/modules/government/catalog.js +13 -17
  49. package/modules/government/migrations/government_019_retire_box_permissions.sql +33 -0
  50. package/modules/government/module.json +1 -0
  51. package/modules/government/protected-surfaces.json +3 -11
  52. package/modules/government/resolver.js +3 -46
  53. package/modules/government/routes/government.js +0 -2
  54. package/modules/government/session-scopes.js +43 -83
  55. package/modules/government/session-scopes.json +8 -18
  56. package/modules/grading/module.json +1 -0
  57. package/modules/hall-ui/module.json +1 -0
  58. package/modules/hall-ui/public/board-lib.js +2 -2
  59. package/modules/hall-ui/public/builders.js +29 -40
  60. package/modules/hall-ui/public/collab.html +1 -1
  61. package/modules/hall-ui/public/collab.js +1 -1
  62. package/modules/hall-ui/public/diagrams.html +3 -3
  63. package/modules/hall-ui/public/dom-utils.js +1 -1
  64. package/modules/hall-ui/public/gate.js +2 -2
  65. package/modules/hall-ui/public/gate.states.json +1 -1
  66. package/modules/hall-ui/public/hall-render.js +43 -79
  67. package/modules/hall-ui/public/index.html +5 -1
  68. package/modules/hall-ui/public/modules.js +1 -0
  69. package/modules/hall-ui/public/oversight.css +5 -5
  70. package/modules/hall-ui/public/palette.js +0 -11
  71. package/modules/hall-ui/public/primer.js +1 -1
  72. package/modules/hall-ui/public/sessions.html +1 -1
  73. package/modules/hall-ui/public/settings-sessions.js +1 -1
  74. package/modules/hall-ui/public/settings.css +0 -1
  75. package/modules/hall-ui/public/settings.html +4 -33
  76. package/modules/hall-ui/public/settings.js +3 -33
  77. package/modules/hall-ui/public/settings.states.json +2 -2
  78. package/modules/hall-ui/public/studio.html +1 -1
  79. package/modules/hall-ui/public/style.css +10 -13
  80. package/modules/hall-ui/public/task.html +4 -1
  81. package/modules/hall-ui/public/task.js +36 -1
  82. package/modules/hall-ui/public/tweak-editor-lib.js +18 -1
  83. package/modules/hall-ui/public/tweak-editor.css +16 -0
  84. package/modules/hall-ui/public/tweak-editor.html +26 -3
  85. package/modules/hall-ui/public/tweak-editor.js +32 -1
  86. package/modules/hall-ui/public/work.js +1 -1
  87. package/modules/hall-ui/records/tweak-editor.md +1 -0
  88. package/modules/ideas/module.json +1 -0
  89. package/modules/lifecycle/db-claim-reads.js +1 -58
  90. package/modules/lifecycle/db-ship.js +0 -5
  91. package/modules/lifecycle/db.js +0 -4
  92. package/modules/lifecycle/kickoff-checklist.js +0 -4
  93. package/modules/lifecycle/lifecycle.js +0 -8
  94. package/modules/lifecycle/module.json +1 -0
  95. package/modules/lifecycle/routes/tasks.js +5 -7
  96. package/modules/memory/module.json +1 -0
  97. package/modules/npm-release/module.json +1 -0
  98. package/modules/onboarding/module.json +1 -0
  99. package/modules/onboarding/onboarding-state.js +17 -36
  100. package/modules/onboarding/routes/access-requests.js +3 -3
  101. package/modules/platform-identity/module.json +1 -0
  102. package/modules/platform-identity/platform-identity.js +13 -5
  103. package/modules/platform-identity/routes/sso.js +2 -2
  104. package/modules/platform-identity/tests/platform-identity.mjs +17 -3
  105. package/modules/provisioning/module.json +1 -0
  106. package/modules/provisioning/provisioning.js +2 -2
  107. package/modules/provisioning/starter-bundles.js +5 -25
  108. package/modules/public-landing/module.json +1 -0
  109. package/modules/public-landing/public/assets/cosmos.css +1 -1
  110. package/modules/public-landing/public/contact.html +1 -1
  111. package/modules/public-landing/public/privacy.html +1 -1
  112. package/modules/public-landing/public/projects.html +101 -32
  113. package/modules/public-landing/public/projects.probes.json +2 -2
  114. package/modules/public-landing/public/projects.states.json +4 -3
  115. package/modules/public-landing/public/terms.html +1 -1
  116. package/modules/security/module.json +1 -0
  117. package/modules/sessions/module.json +1 -0
  118. package/modules/specialities/module.json +1 -0
  119. package/modules/status-ui/module.json +1 -0
  120. package/modules/ui-design/module.json +1 -0
  121. package/package-lock.json +2 -2
  122. package/package.json +1 -1
  123. package/release-notes.json +84 -0
  124. package/scripts/gds/artifact-format.js +203 -0
  125. package/scripts/gds/audit-authorship.sh +1 -13
  126. package/scripts/gds/claim.js +0 -15
  127. package/scripts/gds/cli-lib.js +8 -8
  128. package/scripts/gds/diagram-facts.js +12 -23
  129. package/scripts/gds/do-api.js +13 -80
  130. package/scripts/gds/doc-cli-guard.js +2 -0
  131. package/scripts/gds/doctor.js +1 -1
  132. package/scripts/gds/feedback-latest.js +139 -9
  133. package/scripts/gds/fitness-checks-identity.js +2 -7
  134. package/scripts/gds/fitness-checks-write-validation.js +4 -0
  135. package/scripts/gds/fitness-ratchets.js +4 -4
  136. package/scripts/gds/fitness.js +10 -10
  137. package/scripts/gds/gen-diagrams.js +49 -71
  138. package/scripts/gds/http-api-client.js +12 -19
  139. package/scripts/gds/init.js +1 -1
  140. package/scripts/gds/lint-infra-exec.js +1 -1
  141. package/scripts/gds/local-preview-lib.js +8 -39
  142. package/scripts/gds/local-preview.js +2 -15
  143. package/scripts/gds/migration-namespace.js +3 -3
  144. package/scripts/gds/module-artifact.js +212 -0
  145. package/scripts/gds/module.js +95 -6
  146. package/scripts/gds/package-core.js +5 -116
  147. package/scripts/gds/provision-config.js +2 -2
  148. package/scripts/gds/provision-net.js +2 -3
  149. package/scripts/gds/provision.js +9 -9
  150. package/scripts/gds/publish-manifest.js +1 -2
  151. package/scripts/gds/regen-instance-docs.js +1 -1
  152. package/scripts/gds/run-unit-tests.js +2 -16
  153. package/scripts/gds/sandbox-stage.js +39 -341
  154. package/scripts/gds/ship-flow.js +6 -6
  155. package/scripts/gds/ship-land.js +3 -4
  156. package/scripts/gds/ship-regen.js +10 -41
  157. package/scripts/gds/ship.js +4 -24
  158. package/scripts/gds/start.js +0 -15
  159. package/scripts/hall-preview/README.md +5 -5
  160. package/scripts/hall-preview/server.js +6 -6
  161. package/scripts/render-diagrams.sh +1 -1
  162. package/src/bongos/api-errors.js +1 -1
  163. package/src/bongos/auth-github.js +2 -3
  164. package/src/bongos/auth.js +51 -126
  165. package/src/bongos/db-kernel.js +2 -3
  166. package/src/bongos/db.js +5 -28
  167. package/src/bongos/module-scope-map.js +14 -29
  168. package/src/bongos/module-store.js +141 -0
  169. package/src/bongos/route-rank-check.js +3 -16
  170. package/src/bongos/routes/auth.js +0 -116
  171. package/src/bongos/routes/builders.js +5 -15
  172. package/src/bongos/routes/instance.js +8 -14
  173. package/src/bongos/routes/me.js +6 -22
  174. package/src/bongos/routes/modules.js +83 -0
  175. package/src/bongos/routes.js +5 -0
  176. package/src/module-api.js +13 -6
  177. package/src/module-loader/catalog.js +3 -0
  178. package/src/module-loader/loader.js +4 -4
  179. package/src/module-loader/manifest-schema.js +15 -7
  180. package/src/module-seams.js +2 -3
  181. package/src/modules.js +42 -14
  182. package/tests/agents_spend_guard.mjs +1 -1
  183. package/tests/api_client.mjs +0 -15
  184. package/tests/auth_page_gate.mjs +10 -10
  185. package/tests/canonical_profile_url.mjs +1 -3
  186. package/tests/claim_action.mjs +4 -1
  187. package/tests/claim_from_task_and_home.mjs +127 -0
  188. package/tests/consumer_layout_boot.mjs +2 -0
  189. package/tests/copy_desk_page_docx.mjs +33 -2
  190. package/tests/core_upgrade_runner.mjs +6 -6
  191. package/tests/deploy_divergence_line.mjs +11 -11
  192. package/tests/diagram_facts_offset.mjs +1 -1
  193. package/tests/do_api.mjs +106 -0
  194. package/tests/feedback_latest.mjs +122 -0
  195. package/tests/fitness.mjs +44 -42
  196. package/tests/gate_approvals.mjs +0 -1
  197. package/tests/go_live.mjs +3 -3
  198. package/tests/government_abuse_matrix.mjs +20 -19
  199. package/tests/government_ownership_scope.mjs +33 -35
  200. package/tests/government_parity.mjs +2 -2
  201. package/tests/government_protected_surfaces.mjs +56 -37
  202. package/tests/government_require_permission.mjs +3 -3
  203. package/tests/government_seed.mjs +37 -4
  204. package/tests/government_session_scope.mjs +92 -366
  205. package/tests/hall_error_envelope.mjs +4 -0
  206. package/tests/hall_landing_boot.mjs +4 -1
  207. package/tests/hall_palette.mjs +1 -16
  208. package/tests/hall_record_world.mjs +6 -0
  209. package/tests/hall_tweak_editor.mjs +52 -1
  210. package/tests/host_topology_skips.mjs +1 -2
  211. package/tests/html_comment_nesting.mjs +137 -0
  212. package/tests/infra_exec_paths.mjs +9 -10
  213. package/tests/init.mjs +4 -5
  214. package/tests/instance_manifest.mjs +18 -25
  215. package/tests/kickoff_checklist.mjs +1 -1
  216. package/tests/lib_sh_resolution.mjs +0 -221
  217. package/tests/local_preview.mjs +0 -12
  218. package/tests/module-scope-map.mjs +9 -9
  219. package/tests/module_catalog.mjs +1 -1
  220. package/tests/module_cli.mjs +15 -15
  221. package/tests/module_contributions.mjs +6 -6
  222. package/tests/module_loader.mjs +6 -6
  223. package/tests/module_manifest.mjs +13 -4
  224. package/tests/module_store_publish.mjs +307 -0
  225. package/tests/module_store_publish_route.mjs +121 -0
  226. package/tests/module_store_registry_migration.mjs +70 -0
  227. package/tests/modules.mjs +56 -26
  228. package/tests/onboarding_route_signals.mjs +21 -31
  229. package/tests/onboarding_state.mjs +35 -49
  230. package/tests/permission_path.mjs +0 -3
  231. package/tests/platform_boot.mjs +1 -2
  232. package/tests/profile_route.mjs +1 -6
  233. package/tests/projects_hub.mjs +5 -5
  234. package/tests/projects_hub_app_step.mjs +138 -0
  235. package/tests/projects_hub_module_picker.mjs +2 -2
  236. package/tests/projects_hub_pre_uat.mjs +4 -5
  237. package/tests/provision.mjs +7 -7
  238. package/tests/provisioning_starter_bundles.mjs +0 -22
  239. package/tests/publish_branch_route.mjs +10 -18
  240. package/tests/publish_manifest.mjs +1 -2
  241. package/tests/rank_tier_single_source.mjs +1 -13
  242. package/tests/repo_map.mjs +3 -4
  243. package/tests/runner_drift.mjs +3 -21
  244. package/tests/sandbox_stage.mjs +57 -390
  245. package/tests/session_rename_fallback.mjs +0 -21
  246. package/tests/ship_error_shape.mjs +1 -1
  247. package/tests/task_visual_slots.mjs +31 -0
  248. package/tests/terms_acceptance.mjs +1 -1
  249. package/tests/upgrade.mjs +4 -4
  250. package/tests/watch_sealed_floor.mjs +0 -1
  251. package/tests/wizard_draft_resume.mjs +2 -2
  252. package/tests/wizard_intent_resume.mjs +21 -10
  253. package/tests/wizard_preselect_why.mjs +18 -18
  254. package/docs/onboarding/browser-terminal-guide.md +0 -73
  255. package/docs/recipes/managed-settings-remote-control.md +0 -416
  256. package/modules/dev-box/CLAUDE.md +0 -15
  257. package/modules/dev-box/box-access.js +0 -623
  258. package/modules/dev-box/box-credential.js +0 -94
  259. package/modules/dev-box/box-onboard.js +0 -486
  260. package/modules/dev-box/boxes.js +0 -1038
  261. package/modules/dev-box/db.js +0 -39
  262. package/modules/dev-box/module.json +0 -19
  263. package/modules/dev-box/routes/box.js +0 -1223
  264. package/scripts/gds/box-auth-check.js +0 -160
  265. package/scripts/gds/box-infra.js +0 -321
  266. package/scripts/gds/box-sync.js +0 -216
  267. package/scripts/gds/box.js +0 -1332
  268. package/scripts/gds/cf-tunnel.js +0 -558
  269. package/scripts/gds/smoke-box.sh +0 -150
  270. package/scripts/gds/tree-preflight.js +0 -241
  271. package/src/bongos/app-pair.js +0 -172
  272. package/tests/app_pair.mjs +0 -168
  273. package/tests/box_access.mjs +0 -578
  274. package/tests/box_auth_check.mjs +0 -114
  275. package/tests/box_code_staleness.mjs +0 -129
  276. package/tests/box_connect_e2e.mjs +0 -196
  277. package/tests/box_cost.mjs +0 -70
  278. package/tests/box_credential.mjs +0 -125
  279. package/tests/box_dns_repoint.mjs +0 -153
  280. package/tests/box_env.mjs +0 -64
  281. package/tests/box_host_keys.mjs +0 -126
  282. package/tests/box_infra_resolve.mjs +0 -246
  283. package/tests/box_onboard.mjs +0 -317
  284. package/tests/box_scope_session.mjs +0 -119
  285. package/tests/box_sweep_docs.mjs +0 -119
  286. package/tests/box_sync.mjs +0 -55
  287. package/tests/box_sync_scope_report.mjs +0 -126
  288. package/tests/box_task_scope.mjs +0 -169
  289. package/tests/box_task_scope_pause.mjs +0 -66
  290. package/tests/box_terminal_ssrf.mjs +0 -96
  291. package/tests/boxes.mjs +0 -1585
  292. package/tests/cf_ruleset_preflight.mjs +0 -142
  293. package/tests/cf_tunnel.mjs +0 -496
  294. package/tests/docs_scrubber_damage.mjs +0 -111
  295. package/tests/module_scope_active_claims.mjs +0 -94
  296. package/tests/ship_api_client_pathspec.mjs +0 -100
  297. package/tests/tree_preflight.mjs +0 -243
@@ -3,8 +3,8 @@
3
3
  // /feedback Claude Code slash command.
4
4
  //
5
5
  // Resolves the LATEST feedback bundle the Bongos app wrote to
6
- // ~/CloudBongos/feedback/ (via the shared `latest` pointer in
7
- // bongos-app/src/handoff.js) and prints it for a Claude Code session:
6
+ // ~/CloudBongos/feedback/ (via the shared `latest` pointer, ADR 0087) and
7
+ // prints it for a Claude Code session:
8
8
  //
9
9
  // 1. the full prompt.md body (the model-agnostic transcript + ask, R11), and
10
10
  // 2. the ABSOLUTE path of every attached frame, on its own line, so Claude's
@@ -20,13 +20,139 @@
20
20
  //
21
21
  // Local + read-only: it reads files under your home dir. No GDS session, no
22
22
  // network — so it works on any machine the Bongos app runs on.
23
+ //
24
+ // resolveLatest + feedbackBaseDir are ported here from the app's
25
+ // bongos-app/src/handoff.js, which no longer lives in this repo (the desktop
26
+ // apps publish off-repo, ADR 0098 — task 1004356). The contract is ADR 0087's:
27
+ // `latest.json` is authoritative, the `latest/` symlink is a convenience that
28
+ // may be absent, and with neither the reader self-heals by scanning for the
29
+ // newest bundle dir holding a prompt.md.
23
30
 
24
31
  'use strict';
25
32
 
33
+ const fs = require('node:fs/promises');
34
+ const os = require('node:os');
26
35
  const path = require('node:path');
27
- const { resolveLatest, feedbackBaseDir } = require(
28
- path.join(__dirname, '..', '..', 'bongos-app', 'src', 'handoff.js'),
29
- );
36
+
37
+ const IMAGE_EXT = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif']);
38
+
39
+ /** Where the Bongos app writes bundles: ~/CloudBongos/feedback. */
40
+ function feedbackBaseDir() {
41
+ return path.join(os.homedir(), 'CloudBongos', 'feedback');
42
+ }
43
+
44
+ async function isBundleDir(dir) {
45
+ try {
46
+ return (await fs.stat(path.join(dir, 'prompt.md'))).isFile();
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ // The dir `latest.json` names, or null if it is missing, unreadable or stale.
53
+ // Accepts the pointer by `dir` (absolute) or by `id` (a child of baseDir).
54
+ async function fromPointer(baseDir) {
55
+ let ptr;
56
+ try {
57
+ ptr = JSON.parse(await fs.readFile(path.join(baseDir, 'latest.json'), 'utf8'));
58
+ } catch {
59
+ return null;
60
+ }
61
+ if (!ptr || typeof ptr !== 'object') return null;
62
+ const candidates = [];
63
+ if (typeof ptr.dir === 'string' && ptr.dir) candidates.push(path.resolve(baseDir, ptr.dir));
64
+ if (typeof ptr.id === 'string' && ptr.id && !/[\\/]/.test(ptr.id)) candidates.push(path.join(baseDir, ptr.id));
65
+ for (const dir of candidates) if (await isBundleDir(dir)) return dir;
66
+ return null;
67
+ }
68
+
69
+ async function fromSymlink(baseDir) {
70
+ const link = path.join(baseDir, 'latest');
71
+ if (!(await isBundleDir(link))) return null;
72
+ try {
73
+ return await fs.realpath(link);
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+
79
+ // Self-heal: the newest child dir (by prompt.md mtime, then name) with a prompt.md.
80
+ async function fromScan(baseDir) {
81
+ let entries;
82
+ try {
83
+ entries = await fs.readdir(baseDir, { withFileTypes: true });
84
+ } catch {
85
+ return null;
86
+ }
87
+ let best = null;
88
+ for (const e of entries) {
89
+ if (!e.isDirectory() || e.name === 'latest') continue;
90
+ const dir = path.join(baseDir, e.name);
91
+ let st;
92
+ try {
93
+ st = await fs.stat(path.join(dir, 'prompt.md'));
94
+ } catch {
95
+ continue;
96
+ }
97
+ if (!st.isFile()) continue;
98
+ if (!best || st.mtimeMs > best.mtimeMs || (st.mtimeMs === best.mtimeMs && e.name > best.name)) {
99
+ best = { dir, name: e.name, mtimeMs: st.mtimeMs };
100
+ }
101
+ }
102
+ return best && best.dir;
103
+ }
104
+
105
+ // The bundle's frames as absolute paths: those prompt.md references (in the
106
+ // order the walkthrough cites them) that exist, then any other image in the dir.
107
+ async function collectImages(dir, promptMarkdown) {
108
+ const out = [];
109
+ const seen = new Set();
110
+ const add = (p) => {
111
+ const key = process.platform === 'win32' ? p.toLowerCase() : p;
112
+ if (!seen.has(key)) { seen.add(key); out.push(p); }
113
+ };
114
+ for (const m of promptMarkdown.matchAll(/!\[[^\]]*\]\(\s*<?([^)>]+?)>?\s*\)/g)) {
115
+ const p = path.resolve(dir, m[1]);
116
+ if (!IMAGE_EXT.has(path.extname(p).toLowerCase())) continue;
117
+ try {
118
+ if ((await fs.stat(p)).isFile()) add(p);
119
+ } catch { /* referenced but absent — skip */ }
120
+ }
121
+ const walk = async (d, depth) => {
122
+ let entries;
123
+ try {
124
+ entries = await fs.readdir(d, { withFileTypes: true });
125
+ } catch {
126
+ return;
127
+ }
128
+ entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
129
+ for (const e of entries) {
130
+ const p = path.join(d, e.name);
131
+ if (e.isDirectory() && depth > 0) await walk(p, depth - 1);
132
+ else if (e.isFile() && IMAGE_EXT.has(path.extname(e.name).toLowerCase())) add(p);
133
+ }
134
+ };
135
+ await walk(dir, 1);
136
+ return out;
137
+ }
138
+
139
+ /**
140
+ * The newest feedback bundle under baseDir, or null if there is none.
141
+ * @returns {Promise<null|{id:string,dir:string,promptPath:string,promptMarkdown:string,imagePaths:string[]}>}
142
+ */
143
+ async function resolveLatest(baseDir = feedbackBaseDir()) {
144
+ const dir = (await fromPointer(baseDir)) || (await fromSymlink(baseDir)) || (await fromScan(baseDir));
145
+ if (!dir) return null;
146
+ const promptPath = path.join(dir, 'prompt.md');
147
+ const promptMarkdown = await fs.readFile(promptPath, 'utf8');
148
+ return {
149
+ id: path.basename(dir),
150
+ dir,
151
+ promptPath,
152
+ promptMarkdown,
153
+ imagePaths: await collectImages(dir, promptMarkdown),
154
+ };
155
+ }
30
156
 
31
157
  function parseArgs(argv) {
32
158
  const out = { json: false, base: undefined };
@@ -88,7 +214,11 @@ async function main() {
88
214
  process.stdout.write(`${lines.join('\n')}\n`);
89
215
  }
90
216
 
91
- main().catch((err) => {
92
- process.stderr.write(`feedback-latest: ${err.message}\n`);
93
- process.exitCode = 1;
94
- });
217
+ module.exports = { resolveLatest, feedbackBaseDir };
218
+
219
+ if (require.main === module) {
220
+ main().catch((err) => {
221
+ process.stderr.write(`feedback-latest: ${err.message}\n`);
222
+ process.exitCode = 1;
223
+ });
224
+ }
@@ -71,10 +71,6 @@ const IDENTITY_SCAN_EXTRA_FILES = [];
71
71
  // one, and any file NOT listed must have zero hits. Getting better never fails.
72
72
  //
73
73
  // Why each is legitimate:
74
- // * box.js — REAL Cloudflare/DNS zone names for the operator plane
75
- // (`dev.<host>`, the terminal zone). These are infrastructure identifiers,
76
- // not an instance URL; routing them through branding would be wrong. (Its
77
- // two genuine API-base defaults WERE fixed in 1002428.)
78
74
  // * publish-manifest.js — the literal FILENAMES of three real committed docs
79
75
  // (the two decouple recipes, plus the instance's own name/trademark ADR that
80
76
  // task 1003869 excluded: its prose redacts cleanly but the FILENAME rides in
@@ -91,8 +87,8 @@ const IDENTITY_SCAN_EXTRA_FILES = [];
91
87
  // 1002567). Each is a HOST-PINNED OPS script, not portable code: they name the
92
88
  // live box's actual Postgres database/role and its actual /etc secrets dir, and
93
89
  // every one of those literals is already only the DEFAULT behind an env override
94
- // (GDS_BACKUP_DB, PROD_DB/STAGING_DB, PG_SOCKET). Same rationale as box.js's real
95
- // DNS zones — an infrastructure identifier, not an instance URL, and the systemd
90
+ // (GDS_BACKUP_DB, PROD_DB/STAGING_DB, PG_SOCKET). Each is an infrastructure
91
+ // identifier, not an instance URL, and the systemd
96
92
  // units on that box invoke these by path. Re-pointing an instance's backups is
97
93
  // deployment work, not a branding read. Their COUNT is still frozen here, so a
98
94
  // NEW literal in any of them fails exactly like one in an unlisted file.
@@ -116,7 +112,6 @@ const IDENTITY_SCAN_ALLOWED = {
116
112
  // instance it declines to run instead of destroying the wrong database.
117
113
  'scripts/gds/seed-staging.sh': 4,
118
114
  'scripts/gds/backfill-sessions.js': 2,
119
- 'scripts/gds/box.js': 3,
120
115
  'scripts/gds/publish-manifest.js': 3,
121
116
  'scripts/gds/seed-bongos-coreB-tranche1-tasks.js': 1,
122
117
  'scripts/gds/seed-bongos-coreB-tranche2-tasks.js': 1,
@@ -46,6 +46,10 @@ const WRITE_VALIDATION_EXEMPT = new Set([
46
46
  // the route's own express.raw parser and read by docx.readUpload, which refuses
47
47
  // anything that is not a bounded, readable Word file (not_a_docx / upload_too_large).
48
48
  'POST /copy-desk/pages/:pageId/draft.docx',
49
+ // Not a JSON body either: the raw module tarball (task 1004271, ADR 0338 D1), capped by
50
+ // the route's express.raw parser and read by module-artifact.verifyModuleArtifact, which
51
+ // recomputes every hash and refuses anything that is not a well-formed module version.
52
+ 'POST /store/modules/:key/versions',
49
53
  ]);
50
54
 
51
55
  // KNOWN GAPS, NOT EXEMPTIONS (task 1002551 / BV1.R109b → tracked by task 1002552).
@@ -284,9 +284,9 @@ function knipIssueCount() {
284
284
  // baseline map leaves every `floor === undefined`, and compareToBaselines classifies
285
285
  // an absent floor as a WARNING — so a checkout with no config/ reported the ratchet
286
286
  // as PASSED. Measured: with the baselines gone, a planted 5,000-line and 94-cycle
287
- // regression produced 0 violations. That is not a hypothetical checkout — `config/`
288
- // is absent from every dev box's sparse slice, so the builders who most needed the
289
- // gate were the ones silently exempt from it, learning from red CI instead.
287
+ // regression produced 0 violations. That was not a hypothetical checkout — a sparse
288
+ // checkout without `config/` once left the builders who most needed the gate
289
+ // silently exempt from it, learning from red CI instead.
290
290
  //
291
291
  // Distinguish the two absences, because they are not the same fact:
292
292
  // - the FILE is missing/malformed -> cannot verify anything -> hard fail (here);
@@ -315,7 +315,7 @@ function readBaselines(baselinePath = BASELINE_PATH) {
315
315
  metrics: {},
316
316
  error: missing
317
317
  ? `config/fitness-baselines.json is MISSING — the quality ratchet cannot verify anything, so it fails CLOSED rather than reporting a pass it did not check. `
318
- + `If this is a dev box, config/ was historically outside the sparse checkout (task 1003227); re-sync the box (\`node scripts/gds/box-sync.js\`). `
318
+ + `If this is a sparse or partial checkout, take the whole tree (\`git sparse-checkout disable\`). `
319
319
  + `To bootstrap a fresh baseline deliberately: node scripts/gds/fitness-ratchets.js --update-baselines`
320
320
  : `config/fitness-baselines.json is unreadable (${e && e.message}) — failing CLOSED.`,
321
321
  };
@@ -343,11 +343,11 @@ function checkDeterministicRoutines() {
343
343
  // published doorway); the core NEVER imports a module; a module NEVER imports a
344
344
  // sibling module. These two checks enforce it.
345
345
  //
346
- // Both were GREEN through the strangler: dev-box + art-pipeline (the M07/M08
347
- // moves) import only the doorway, and the core imports neither. The boundary
346
+ // Both were GREEN through the strangler: the first two moved modules (M07/M08)
347
+ // imported only the doorway, and the core imported neither. The boundary
348
348
  // shipped in WARN mode so a half-moved module mid-migration couldn't red the
349
349
  // build; BV1.R45 (task 1414) flips it to ENFORCING now that the first move
350
- // (dev-box, M07) proved clean — the monotonic ratchet (ADR 0062 §9 / design
350
+ // (M07) proved clean — the monotonic ratchet (ADR 0062 §9 / design
351
351
  // 04§3). A NEW cross-boundary import is now a red build; warn-mode is gone.
352
352
  let MODULE_BOUNDARY_ENFORCING = true;
353
353
  const MODULE_API_REL = 'src/module-api.js';
@@ -497,7 +497,7 @@ function checkScopeMapImportsProtected() {
497
497
  // module-AGNOSTIC: it discovers, validates, and mounts modules uniformly and may
498
498
  // never special-case one by name. The import checks (Check 7/8) stop a module's
499
499
  // CODE from being reached by `require`; this stops a module's IDENTITY from being
500
- // hardcoded into the kernel (`if (key === 'dev-box') …`, a copied module helper,
500
+ // hardcoded into the kernel (`if (key === 'art-pipeline') …`, a copied module helper,
501
501
  // a registry entry that should have moved out). "No module-specific code" is
502
502
  // operationalized as the one drift-free signal — the kernel names no discovered
503
503
  // module KEY (the canonical id from modules/<key>/module.json). Domain terms are
@@ -606,14 +606,14 @@ const KERNEL_FILES = [
606
606
  // Kernel files exempt from the no-module-keys scan (Check 10) because naming a
607
607
  // module is, for them, the JOB — they stay in KERNEL_FILES for the import check
608
608
  // (Check 12), which guards dependency *direction* (a much stronger guarantee than
609
- // "never write the string 'dev-box'"). Two legitimate reasons a kernel file names
609
+ // "never write the string 'art-pipeline'"). Two legitimate reasons a kernel file names
610
610
  // a module:
611
611
  // • the REGISTRY — module-scope-map.js IS the module→path map (and
612
612
  // checkScopeMapImportsProtected already guards how it derives `protected`).
613
613
  // • the TRUST BOUNDARY — auth.js carries the Discord OAuth account-LINK flow
614
614
  // (core by design — see modules/discord/CLAUDE.md "NOT in this module"); and
615
615
  // permission-path-check.js / route-rank-check.js MUST name module path
616
- // subtrees ('modules/dev-box/', …) to protect + rank-gate them. Forbidding
616
+ // subtrees ('modules/grading/', …) to protect + rank-gate them. Forbidding
617
617
  // these the string would force the trust layer to stop protecting module
618
618
  // surfaces — exactly backwards. The generic machinery + data + config files
619
619
  // (module-api, the loader, db-kernel, secret-box, pool, _helpers, branding,
@@ -683,8 +683,8 @@ function moduleDir(key) {
683
683
  return path.join(CORE_ROOT, 'modules', key);
684
684
  }
685
685
 
686
- // Match a module key as a DELIMITED token so `dev-box` hits in `'dev-box'` /
687
- // `mount('dev-box')` but never inside `dev-boxes` or `xdev-box`. Hyphens are
686
+ // Match a module key as a DELIMITED token so `art-pipeline` hits in `'art-pipeline'` /
687
+ // `mount('art-pipeline')` but never inside `art-pipelines` or `xart-pipeline`. Hyphens are
688
688
  // non-word chars, so the boundary guards against word-chars AND hyphens on both
689
689
  // sides (no lookbehind — portable on every node the unit gate runs).
690
690
  function keyTokenRe(key) {
@@ -816,7 +816,7 @@ function checkKernelImportsNoDomain() {
816
816
  // migrations," never "drop its tables" (ADR 0083 §Module-owned migrations);
817
817
  // there is no down path, so a DROP/TRUNCATE can only be an accident.
818
818
  // • NAMESPACED — every object it CREATEs lives under the <key>_ prefix
819
- // (dev-box → dev_box_), and it may only ALTER its own namespaced tables;
819
+ // (art-pipeline → art_pipeline_), and it may only ALTER its own namespaced tables;
820
820
  // core/sibling state is reached through the doorway/seams, never raw DDL.
821
821
  //
822
822
  // LEGACY_NAMESPACED: tables that predate the <key>_-prefix convention (created before the module
@@ -848,7 +848,7 @@ const CREATE_OBJ_RE = /\bcreate\s+(?:or\s+replace\s+)?(?:unique\s+)?(?:materiali
848
848
  const CREATE_INDEX_RE = /\bcreate\s+(?:unique\s+)?index\s+(?:concurrently\s+)?(?:if\s+not\s+exists\s+)?(?:"?[\w.]+"?)\s+on\s+(?:only\s+)?("?[\w.]+"?)/gi;
849
849
  const ALTER_TABLE_RE = /\balter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?("?[\w.]+"?)/gi;
850
850
 
851
- // SQL object-namespace prefix for a module key: dev-box → dev_box (SQL-safe).
851
+ // SQL object-namespace prefix for a module key: art-pipeline → art_pipeline (SQL-safe).
852
852
  function moduleNamespace(key) { return key.replace(/-/g, '_'); }
853
853
 
854
854
  // strip SQL comments so a commented-out statement is never scanned
@@ -8,9 +8,9 @@
8
8
  // 05-versioning ← live versions (id/status)
9
9
  // 03-drachmae-karma ← per-piece backing-task status + seeded-achievement count
10
10
  // 04-architecture ← per-piece backing-task status: the ADR 0024 memory
11
- // chain + the dev-box / box→prod ship flow, merged into
12
- // one "complete architecture" map (was 04-server-repo-
13
- // memory + 06-dev-box; consolidated under task 1059)
11
+ // chain + the machine → prod ship flow, in one
12
+ // "complete architecture" map (consolidated under
13
+ // task 1059)
14
14
  //
15
15
  // It writes each .mmd from a template, renders the SVG (crisp, pannable) + PNG
16
16
  // (fallback) via scripts/render-diagrams.sh, and rewrites the data-driven slices
@@ -332,17 +332,18 @@ function drachmaeKarmaMmd(f) {
332
332
  }
333
333
 
334
334
  // 04-architecture. The complete picture: where a builder's work runs and how it
335
- // reaches production, with the per-builder memory layer (ADR 0024) folded in.
336
- // Merges the old 04-server-repo-memory (the three layers) + 06-dev-box (the box
337
- // → prod ship flow) into one map (task 1059), collapsing the SERVER they both
338
- // drew. Conceptual like 03: every feature-piece + flow edge is LIVE/solid iff its
339
- // backing Bongos task is 'shipped', dotted/PLANNED otherwise — so the map is an
340
- // honest snapshot that flips itself as pieces ship (or regresses if one is
335
+ // reaches production, with the per-builder memory layer (ADR 0024) folded in
336
+ // (task 1059). Conceptual like 03: every feature-piece + flow edge is LIVE/solid
337
+ // iff its backing Bongos task is 'shipped', dotted/PLANNED otherwise — so the map
338
+ // is an honest snapshot that flips itself as pieces ship (or regresses if one is
341
339
  // reopened). Pieces → live task ids:
342
340
  // memory layer : 273 schema · 274 sync-on-ship · 275 pull-on-start · 276 isolation
343
- // dev box : 904 desktop app · 774 browser terminal · 897 sandbox preview · 919 box token
344
- // box → prod : 1025 publish-via-server · 859 CI deploy · 1028 GitHub App push credential
345
- // end-to-end : 1027 hardware-verify (no node of its own; gates the closing note)
341
+ // ship flow : 1025 publish-via-server · 1028 GitHub App push credential
342
+ // Work runs on the builder's own machine, in a clone of the instance repo. It
343
+ // reaches main one of two ways (ship-deploy-target.js `pushVia`): a machine
344
+ // signed in to GitHub pushes its branch and opens the PR itself; a checkout with
345
+ // no push credential uploads the branch as a git bundle and the SERVER pushes it
346
+ // with its own credential (ADR 0055).
346
347
  function architectureMmd(f) {
347
348
  const c = f.conceptual;
348
349
  const { host } = brand();
@@ -370,16 +371,16 @@ function architectureMmd(f) {
370
371
  ? 'SRVMEM -->|"pull on session start"| LOCAL'
371
372
  : 'SRVMEM -.->|"pull on session start — PLANNED"| LOCAL';
372
373
 
373
- // --- box → prod credential + closing note ---
374
+ // --- ship flow: the server's push credential + closing note ---
374
375
  const pushCred = shipped(1028)
375
376
  ? 'GitHub App, short-lived tokens'
376
377
  : 'standing push PAT (GitHub App pending — 1028)';
377
- // The whole box→prod chain LIVE? (all the gated legs + the end-to-end verify.)
378
- // NB the deploy leg is the standalone AUTO-PULL (ADR 0108/0129), not a task-gated
379
- // GitHub-Actions CI job — so 859 (CI deploy) is intentionally NOT in this set.
380
- const chainLive = [919, 897, 904, 774, 1025, 1028, 1027].every(shipped);
378
+ // Both ways onto main LIVE? NB the deploy leg is the standalone AUTO-PULL
379
+ // (ADR 0108/0129), not a task-gated GitHub-Actions CI job — so 859 (CI deploy)
380
+ // is intentionally NOT in this set.
381
+ const chainLive = [1025, 1028].every(shipped);
381
382
  const closingNote = chainLive
382
- ? 'The whole box → production path is LIVE: a credential-less box uploads its branch, the server merges it, and the instance auto-pulls + redeploys — hardware-verified end-to-end (1027 · 1028).'
383
+ ? 'Both ways onto main work today: a machine signed in to GitHub pushes its own branch, and a checkout without a push credential uploads it for the server to push. Either way the instance auto-pulls and redeploys.'
383
384
  : `Still landing (PLANNED): the server-mediated publish (${tag(1025)} · 1025) and the GitHub App push credential (${tag(1028)} · 1028).`;
384
385
 
385
386
  // Product-neutral runtime label: the instance's own product name (branding),
@@ -389,30 +390,22 @@ function architectureMmd(f) {
389
390
  const appLabel = productName && productName !== 'Cloud Bongos' ? productName : 'app';
390
391
 
391
392
  // Layout: ELK engine, top-down (task 1066). ELK minimizes edge crossings far
392
- // better than the default renderer — this map is edge-dense and was a tangle
393
- // before. Two more declutter moves: (1) the INTRA-cluster wiring (worker→its
394
- // files, api→db/rank, db→memory-store) is dropped — co-location inside a
395
- // labelled subgraph already says "these belong together", and drawing it added
396
- // depth + crossings; only the CROSS-cluster flow (the numbered ship steps,
397
- // memory sync, claim/ship, the blocked direct-push) is drawn. (2) Three
398
- // low-signal annotation edges (box-token→bootstrap, DB-is-king, memory-
399
- // follows-you) are folded into node labels. (3) WATERFALL ordering (task 1073,
400
- // off Lars's "put the GitHub box last"): subgraphs are declared in flow order
401
- // DEVICE→BOX→SERVER→GH, and the live app (the instance's product runtime) is
402
- // pulled OUT of the SERVER cluster into its own terminal node at the very
403
- // bottom (annotated "same server · same Node process" so it stays honest).
404
- // That makes the map one monotonic top-down spine — device→box→server→github→
405
- // live app — where the deploy is the LAST forward step instead of an edge
406
- // looping back UP into the server. So SERVER tucks directly under BOX and the
407
- // BOX↔SERVER wires (re-auth, upload, sync, pull, claim/ship) become short hops
408
- // instead of detouring the full height past a mid-stack GitHub. The deploy is
409
- // the STANDALONE auto-pull (ADR 0108/0129): merge to the instance's own main →
410
- // the server pulls its own repo → migrate/regenerate/restart → the live app —
411
- // NOT a GitHub-Actions push-deploy (that was the reference monorepo's model).
412
- // NOTE on direction: we keep TB,
413
- // not a repo-on-the-left LR/RL — the flow is an ~8-deep linear chain, so a
414
- // sideways layout stretches into an unreadable ~5:1 strip; top-down keeps a
415
- // legible aspect.
393
+ // better than the default renderer. Two declutter moves: (1) the INTRA-cluster
394
+ // wiring (worker→its files, api→db/rank, db→memory-store) is dropped —
395
+ // co-location inside a labelled subgraph already says "these belong together";
396
+ // only the CROSS-cluster flow (the numbered ship steps, memory sync,
397
+ // claim/ship) is drawn. (2) Low-signal annotation edges (DB-is-king,
398
+ // memory-follows-you) are folded into node labels. WATERFALL ordering (task
399
+ // 1073, off Lars's "put the GitHub box last"): subgraphs are declared in flow
400
+ // order MACHINE→SERVER→GH, and the live app (the instance's product runtime)
401
+ // is pulled OUT of the SERVER cluster into its own terminal node at the very
402
+ // bottom (annotated "same server · same Node process" so it stays honest), so
403
+ // the deploy is the LAST forward step instead of an edge looping back UP into
404
+ // the server. The deploy is the STANDALONE auto-pull (ADR 0108/0129): merge to
405
+ // the instance's own main → the server pulls its own repo →
406
+ // migrate/regenerate/restart → the live app — NOT a GitHub-Actions push-deploy
407
+ // (that was the reference monorepo's model). NOTE on direction: we keep TB,
408
+ // not LR — a sideways layout stretches the chain into an unreadable strip.
416
409
  const lines = [
417
410
  '---',
418
411
  'title: The complete architecture — where your work runs and how it reaches production (AUTO-GENERATED from live facts — do not hand-edit; see scripts/gds/gen-diagrams.js)',
@@ -420,18 +413,10 @@ function architectureMmd(f) {
420
413
  ' layout: elk',
421
414
  '---',
422
415
  'flowchart TB',
423
- ' subgraph DEVICE["YOUR DEVICE — only the screen lives here"]',
424
- ' APP["Claude desktop app or browser tab\\n(shows files · sends your messages · holds NO code)"]',
425
- ' end',
426
- '',
427
- ' subgraph BOX["DEV BOX — a machine WE create on DigitalOcean (gated · revocable · audited; Metic+ may instead clone locally)"]',
428
- ' WORKER["Claude Code WORKER\\n(opens files · runs commands · runs the /builder-* skills)"]',
429
- ` CODE["the code — git clone + per-builder worktrees (.claude/worktrees/*)\\nfor sub-Metic builders it lives ONLY here · CLAUDE.md is DESCRIPTIVE only"]`,
416
+ ' subgraph MACHINE["YOUR MACHINE — a clone of the instance repo"]',
417
+ ' WORKER["Claude Code — the desktop app or the terminal\\n(opens files · runs commands · runs the /builder-* skills)"]',
418
+ ' CODE["the code — git clone + per-task worktrees (.claude/worktrees/*)\\nCLAUDE.md is DESCRIPTIVE only"]',
430
419
  ' LOCAL["local ~/.claude memory\\nMAP = MEMORY.md — a one-line index loaded EVERY session\\n(a guard + auto-tidy keep it light; ADR 0074)\\n+ drawer of full notes, loaded only on demand"]',
431
- ' ORIGIN["credential-less git origin\\n(NO GitHub push secret on the box — by design)"]',
432
- ` BOXTOK["box-scoped Bongos token\\n(deny-by-default: bootstrap endpoints only;\\nrejected for branch upload → re-auth first — ${tag(919)})"]`,
433
- ` PREVIEW["sandbox preview server on :3000\\n→ a per-builder sandbox URL (${tag(897)})"]`,
434
- ` SWITCH["on / off + browser terminal\\n(desktop app ${tag(904)} · ttyd-over-tunnel ${tag(774)})"]`,
435
420
  ' end',
436
421
  '',
437
422
  ` subgraph SERVER["THE INSTANCE SERVER — ${host} (the authority AND production)"]`,
@@ -440,7 +425,7 @@ function architectureMmd(f) {
440
425
  ' DB["Postgres — tasks · claims · versions · builders.rank\\ncredit_log · grades · idea_inbox · builder_memory\\n(KING for work-state + permissions; CLAUDE.md only describes it)"]',
441
426
  ' RANK["auth.requireRank — checked per request, NO caching\\n(ADR 0016: a demotion takes effect on the next request)"]',
442
427
  ` SRVMEM["server-side builder_memory store\\n${memLead}: per-builder · isolated · forensic read at Metic+ (ADR 0157)\\nyour memory follows you across machines & worktrees\\n/recall fetches only the relevant subset (ADR 0060)\\n(${memStatus})"]`,
443
- ` PUSHTOK["server-held push credential\\n(${pushCred} — the box never holds it)"]`,
428
+ ` PUSHTOK["server-held push credential\\n(${pushCred} — for a checkout that has none of its own)"]`,
444
429
  ' DEPLOY["auto-pull deploy (ADR 0129)\\na timer pulls THIS instance repo every ~2 min →\\nnpm ci if the pin changed → migrate → regenerate docs → restart → healthz"]',
445
430
  ' end',
446
431
  '',
@@ -450,17 +435,14 @@ function architectureMmd(f) {
450
435
  '',
451
436
  ` LIVEAPP["the live ${appLabel} — ${host}\\n(same server · same Node process as /api/bongos/*)"]`,
452
437
  '',
453
- ' APP -->|"1 · toggle box on / open terminal"| SWITCH',
454
- ' SWITCH --> WORKER',
455
- ' WORKER -->|"2 · edit + commit"| CODE',
456
- ' WORKER -->|"3 · re-auth in browser"| API',
457
- flow('WORKER', 1025, '4 · upload branch as a git bundle', 'API'),
458
- flow('API', 1025, `5 · push + open PR + auto-merge (${tag(1025)})`, 'MAIN'),
459
- ' MAIN -->|"6 · the instance auto-pulls its own main"| DEPLOY',
460
- ' DEPLOY -->|"7 · migrate · regenerate docs · restart"| LIVEAPP',
438
+ ' WORKER -->|"1 · edit + commit"| CODE',
439
+ ' WORKER -->|"2 · push the branch + open a PR (a machine signed in to GitHub)"| MAIN',
440
+ flow('WORKER', 1025, '2 · or upload the branch as a git bundle (no push credential here)', 'API'),
441
+ flow('API', 1025, `3 · the server pushes it + opens the PR (${tag(1025)})`, 'MAIN'),
442
+ ' MAIN -->|"4 · the instance auto-pulls its own main"| DEPLOY',
443
+ ' DEPLOY -->|"5 · migrate · regenerate docs · restart"| LIVEAPP',
461
444
  ' CORE --- API',
462
445
  ' PUSHTOK -.-> API',
463
- ' ORIGIN -. "direct push blocked" .-> MAIN',
464
446
  ' API -. "claim / ship / cost" .-> WORKER',
465
447
  ` ${pushEdge}`,
466
448
  ` ${pullEdge}`,
@@ -500,20 +482,16 @@ function rebuildAssertions(prev, f) {
500
482
  pending_tasks: ids.filter((id) => !shipped(id)).map(entry),
501
483
  });
502
484
 
503
- // 04-architecture — the ADR 0024 memory chain + the dev-box / box→prod ship
504
- // flow, merged (task 1059). A piece is LIVE iff its backing task is shipped.
505
- const arch = split([273, 274, 275, 276, 904, 774, 897, 919, 1025, 1027, 1028]);
485
+ // 04-architecture — the ADR 0024 memory chain + the machine → prod ship
486
+ // flow (task 1059). A piece is LIVE iff its backing task is shipped.
487
+ const arch = split([273, 274, 275, 276, 1025, 1028]);
506
488
  next.architecture = {
507
489
  diagram: '04-architecture',
508
490
  _comment:
509
- "Pieces of 04-architecture gated on a backing Bongos task. A piece is LIVE iff its task is 'shipped'; live_tasks must ALL be shipped, pending_tasks must NOT yet be. Rebuilt by scripts/gds/gen-diagrams.js in lockstep with the diagram, so this is the drift checker's backstop. The device/box/server/GitHub topology is structural (not task-gated); these are the LIVE/PLANNED markers. MEMORY layer (ADR 0024): 273 schema / 274 sync-on-ship / 275 pull-on-start / 276 isolation. DEV BOX + box→prod: 904/774 (on-off + browser terminal), 897 (sandbox preview), 919 (scoped box token), 1025 (server-mediated publish), 1028 (GitHub App push credential), 1027 (end-to-end hardware-verify). The deploy leg is the standalone AUTO-PULL (ADR 0108/0129), not a task-gated GitHub-Actions CI job, so 859 is intentionally absent. Supersedes the old server_repo_memory + dev_box slices.",
491
+ "Pieces of 04-architecture gated on a backing Bongos task. A piece is LIVE iff its task is 'shipped'; live_tasks must ALL be shipped, pending_tasks must NOT yet be. Rebuilt by scripts/gds/gen-diagrams.js in lockstep with the diagram, so this is the drift checker's backstop. The machine/server/GitHub topology is structural (not task-gated); these are the LIVE/PLANNED markers. MEMORY layer (ADR 0024): 273 schema / 274 sync-on-ship / 275 pull-on-start / 276 isolation. Ship flow: 1025 (server-mediated publish, for a checkout with no push credential), 1028 (GitHub App push credential). The deploy leg is the standalone AUTO-PULL (ADR 0108/0129), not a task-gated GitHub-Actions CI job, so 859 is intentionally absent.",
510
492
  live_tasks: arch.live_tasks,
511
493
  pending_tasks: arch.pending_tasks,
512
494
  };
513
- // Drop the two slices this diagram replaced so the file (and drift checker)
514
- // never carry stale keys after the 04+06 consolidation.
515
- delete next.server_repo_memory;
516
- delete next.dev_box;
517
495
 
518
496
  // 03-drachmae-karma — bounty + karma pieces (D_SHIP/D_ACH have no task gate).
519
497
  const dk = split([284, 286, 282, 245, 248, 246]);
@@ -1,24 +1,18 @@
1
- // scripts/gds/http-api-client.js — the shared skeleton behind the control plane's
2
- // two JSON-over-HTTP clients: scripts/gds/do-api.js (DoApi, DigitalOcean) and
3
- // scripts/gds/cf-tunnel.js (CfTunnelApi, Cloudflare). Both are thin, injectable-
4
- // transport, dry-run-by-default wrappers with a byte-identical request skeleton
5
- // (do-api.js's design note: the box subsystem keeps "ONE shape for every external
6
- // API"). This base IS that one shape.
1
+ // scripts/gds/http-api-client.js — the request skeleton behind the control
2
+ // plane's JSON-over-HTTP client, scripts/gds/do-api.js (DoApi, DigitalOcean): a
3
+ // thin, injectable-transport, dry-run-by-default wrapper. It is kept apart from
4
+ // do-api.js so the ONE request shape stays reusable by any future client.
7
5
  //
8
- // The genuine per-API differences stay in the subclasses via three small hooks:
9
- // - the constructor's OWN live-mode validation (the "missing X" messages and the
10
- // order they fire in differ, and tests assert both);
11
- // - requestInit() — extra fetch options (Cloudflare pins an IPv4 dispatcher,
12
- // #771; DigitalOcean sends none);
6
+ // Per-API differences go in a subclass via three small hooks:
7
+ // - the constructor's OWN live-mode validation (its "missing X" messages);
8
+ // - requestInit() — extra fetch options (DigitalOcean sends none);
13
9
  // - detectError() — what counts as a failure + how the message is extracted
14
- // (DigitalOcean = non-2xx → message/id; Cloudflare also fails the {success:false}
15
- // envelope and joins errors[]).
10
+ // (DigitalOcean = non-2xx → message/id).
16
11
  //
17
- // Behaviour-neutral extraction: pinned by tests/boxes.mjs (DoApi) and
18
- // tests/cf_tunnel.mjs (CfTunnelApi).
12
+ // Pinned by tests/do_api.mjs.
19
13
 
20
14
  // Build a distinct, named API-error class. Each client makes its own so the
21
- // `instanceof` checks the tests rely on (DoApiError vs CfApiError) stay meaningful
15
+ // `instanceof` checks the tests rely on (DoApiError) stay meaningful
22
16
  // and the `.name` / `.code` carried on every thrown error survive unchanged.
23
17
  function makeApiError(name, code) {
24
18
  return class ApiError extends Error {
@@ -46,13 +40,12 @@ class JsonHttpApi {
46
40
  this._ErrorClass = ErrorClass;
47
41
  }
48
42
 
49
- // Extra fetch init merged into every LIVE request. Base sends none; CfTunnelApi
50
- // overrides to pin the IPv4 dispatcher (its token is IP-locked, #771).
43
+ // Extra fetch init merged into every LIVE request. Base sends none.
51
44
  requestInit() { return {}; }
52
45
 
53
46
  // Inspect a completed response. Return an error MESSAGE string to throw, or null
54
47
  // to accept. Base fails on a non-2xx HTTP status (DigitalOcean surfaces
55
- // message/id); CfTunnelApi also fails the Cloudflare {success:false} envelope.
48
+ // message/id).
56
49
  detectError(res, json) {
57
50
  if (!res.ok) return (json && (json.message || json.id)) || `HTTP ${res.status}`;
58
51
  return null;
@@ -79,7 +79,7 @@ const { detectStack, formatStackSummary } = require('./adopt-detect'); // ADR 01
79
79
  const { resolveBacklogTasks, DEFAULT_MAX: BACKLOG_MAX } = require('./adopt-import'); // ADR 0121 §Decision 3 (task 2044): import an existing backlog
80
80
  const { preflightAdopt, formatPreflight } = require('./adopt-preflight'); // ADR 0121 §Decision 5 (task 2045): the accept-gated conflict pre-flight
81
81
 
82
- const KNOWN_MODULES = ['game', 'art-pipeline', 'discord', 'dev-box'];
82
+ const KNOWN_MODULES = ['game', 'art-pipeline', 'discord'];
83
83
 
84
84
  // The provisioning hosting shapes — a small mirror of modules/provisioning's
85
85
  // HOSTING_SHAPES. init deliberately does NOT import the (optional) provisioning
@@ -3,7 +3,7 @@
3
3
  // lint-infra-exec.js — task 2137 (ADR 0108 configurable-root). The dry-run/lint that proves every
4
4
  // host systemd unit running a CORE script still resolves AFTER the R89 split.
5
5
  //
6
- // The units (box-*, cohost-*, provision-*, search-index-db) keep living in the instance repo
6
+ // The units (cohost-*, provision-*, search-index-db) keep living in the instance repo
7
7
  // (infra/ is host topology) while scripts/gds/ moves into node_modules/@bongos/core, so each
8
8
  // ExecStart runs through infra/core-node.sh, which resolves the script under the instance root OR
9
9
  // its node_modules/@bongos/core (both layouts). "Resolves in a consumer layout" is provable from