@bongos/core 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1142) hide show
  1. package/.bongos-core.json +5727 -0
  2. package/.claude/skills/blocker-review/SKILL.md +91 -0
  3. package/.claude/skills/blocker-solve/SKILL.md +96 -0
  4. package/.claude/skills/builder-backup/SKILL.md +54 -0
  5. package/.claude/skills/builder-box/SKILL.md +45 -0
  6. package/.claude/skills/builder-claim/SKILL.md +76 -0
  7. package/.claude/skills/builder-connect/SKILL.md +59 -0
  8. package/.claude/skills/builder-cost/SKILL.md +47 -0
  9. package/.claude/skills/builder-end/SKILL.md +81 -0
  10. package/.claude/skills/builder-exit/SKILL.md +88 -0
  11. package/.claude/skills/builder-key/SKILL.md +40 -0
  12. package/.claude/skills/builder-reauth/SKILL.md +50 -0
  13. package/.claude/skills/builder-redteam/SKILL.md +109 -0
  14. package/.claude/skills/builder-release/SKILL.md +41 -0
  15. package/.claude/skills/builder-setup/SKILL.md +62 -0
  16. package/.claude/skills/builder-ship/SKILL.md +79 -0
  17. package/.claude/skills/builder-stage/SKILL.md +51 -0
  18. package/.claude/skills/builder-start/SKILL.md +46 -0
  19. package/.claude/skills/close-box/SKILL.md +43 -0
  20. package/.claude/skills/design-sync/SKILL.md +69 -0
  21. package/.claude/skills/dev/SKILL.md +49 -0
  22. package/.claude/skills/feedback/SKILL.md +32 -0
  23. package/.claude/skills/figma-design-sync/SKILL.md +74 -0
  24. package/.claude/skills/goal-review/SKILL.md +89 -0
  25. package/.claude/skills/idea-triage/SKILL.md +144 -0
  26. package/.claude/skills/ideate/SKILL.md +82 -0
  27. package/.claude/skills/merge-mode/SKILL.md +169 -0
  28. package/.claude/skills/new-project/SKILL.md +108 -0
  29. package/.claude/skills/otb-character-review/SKILL.md +71 -0
  30. package/.claude/skills/otb-design-review/SKILL.md +47 -0
  31. package/.claude/skills/otb-feedback-capture/SKILL.md +45 -0
  32. package/.claude/skills/otb-figma-sync/SKILL.md +54 -0
  33. package/.claude/skills/otb-tile-generate/SKILL.md +45 -0
  34. package/.claude/skills/paint/SKILL.md +70 -0
  35. package/.claude/skills/planning-session/SKILL.md +299 -0
  36. package/.claude/skills/priority-session/SKILL.md +134 -0
  37. package/.claude/skills/read-session-export/SKILL.md +58 -0
  38. package/.claude/skills/recall/SKILL.md +52 -0
  39. package/.claude/skills/session-handoff/SKILL.md +46 -0
  40. package/.claude/skills/status/SKILL.md +33 -0
  41. package/.devcontainer/Dockerfile +69 -0
  42. package/.devcontainer/README.md +73 -0
  43. package/.devcontainer/devcontainer-lock.json +19 -0
  44. package/.devcontainer/devcontainer.json +93 -0
  45. package/.devcontainer/healthcheck.sh +84 -0
  46. package/.devcontainer/init-firewall.sh +198 -0
  47. package/.devcontainer/post-create.sh +153 -0
  48. package/.dockerignore +36 -0
  49. package/.env.local.example +32 -0
  50. package/.gitattributes +83 -0
  51. package/.gitignore +91 -0
  52. package/CLAUDE.md +242 -0
  53. package/CODE_OF_CONDUCT.md +46 -0
  54. package/CONTRIBUTING.md +51 -0
  55. package/Dockerfile +46 -0
  56. package/GOVERNANCE.md +44 -0
  57. package/LICENSE +661 -0
  58. package/README.md +88 -0
  59. package/bin/bongos.js +222 -0
  60. package/clients/bongos-client/README.md +48 -0
  61. package/clients/bongos-client/bongos-client.global.js +617 -0
  62. package/clients/bongos-client/examples/hello-world.mjs +62 -0
  63. package/clients/bongos-client/index.cjs +619 -0
  64. package/clients/bongos-client/index.d.ts +793 -0
  65. package/clients/bongos-client/index.mjs +614 -0
  66. package/clients/bongos-client/package.json +32 -0
  67. package/config/branding.neutral.json +60 -0
  68. package/config/design-tokens.neutral.json +51 -0
  69. package/config/design-tokens.schema.json +96 -0
  70. package/config/modules.neutral.json +9 -0
  71. package/docker-compose.yml +52 -0
  72. package/docker-entrypoint.sh +30 -0
  73. package/docs/adr/0001-stack-choice-single-vps.md +45 -0
  74. package/docs/adr/0002-digitalocean-over-hetzner.md +27 -0
  75. package/docs/adr/0003-cloudflare-origin-cert-over-letsencrypt.md +35 -0
  76. package/docs/adr/0004-amazonprimea-name-and-trademark-acceptance.md +45 -0
  77. package/docs/adr/0005-invisible-other-players-in-v1.md +37 -0
  78. package/docs/adr/0006-singleton-worldroom-plus-queueroom.md +30 -0
  79. package/docs/adr/0007-persistent-identity-via-localstorage-uuid.md +31 -0
  80. package/docs/adr/0008-family-based-tile-generation.md +86 -0
  81. package/docs/adr/0008-google-chat-oauth-user-auth.md +37 -0
  82. package/docs/adr/0009-autotile-architecture.md +181 -0
  83. package/docs/adr/0010-pms-architecture.md +70 -0
  84. package/docs/adr/0011-terrain-id-rendering-model.md +174 -0
  85. package/docs/adr/0012-ground-object-cell-split.md +208 -0
  86. package/docs/adr/0013-paths-as-object-overlays-with-autotile-dispatch.md +158 -0
  87. package/docs/adr/0014-families-canonical-over-vocabulary.md +103 -0
  88. package/docs/adr/0015-task-dependencies-and-auto-promotion.md +125 -0
  89. package/docs/adr/0016-trust-boundary-server-enforced-permissions.md +132 -0
  90. package/docs/adr/0017-code-simplification-and-repo-cleanliness.md +74 -0
  91. package/docs/adr/0018-three-rank-model-goes-live.md +97 -0
  92. package/docs/adr/0019-red-team-bounty-payout-table.md +52 -0
  93. package/docs/adr/0020-security-model-public-stub.md +70 -0
  94. package/docs/adr/0021-per-builder-skill-model-preferences.md +99 -0
  95. package/docs/adr/0022-secrets-policy.md +124 -0
  96. package/docs/adr/0023-kind-multipliers-and-idea-bonus.md +118 -0
  97. package/docs/adr/0024-cloneable-repo-local-first-memory.md +277 -0
  98. package/docs/adr/0024-multi-agent-system-architecture.md +125 -0
  99. package/docs/adr/0025-offsite-backup-vendor-digitalocean-spaces.md +59 -0
  100. package/docs/adr/0025-structured-criterion-task-link.md +44 -0
  101. package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +117 -0
  102. package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +170 -0
  103. package/docs/adr/0028-honest-llm-spend-accounting.md +109 -0
  104. package/docs/adr/0029-status-page-outage-resilience.md +59 -0
  105. package/docs/adr/0030-builders-hall-live-updates.md +59 -0
  106. package/docs/adr/0031-cloud-dev-environments-for-builders.md +222 -0
  107. package/docs/adr/0032-discord-mirror-of-gds.md +50 -0
  108. package/docs/adr/0033-discord-bot-service-principal.md +82 -0
  109. package/docs/adr/0034-thetes-graduated-newcomer-rank.md +53 -0
  110. package/docs/adr/0035-builder-onboarding-three-paths.md +128 -0
  111. package/docs/adr/0036-builders-subdomain-and-ui-reorg.md +40 -0
  112. package/docs/adr/0037-discord-channel-management.md +51 -0
  113. package/docs/adr/0038-chromebook-ttyd-cloudflare-tunnel.md +111 -0
  114. package/docs/adr/0039-setup-first-onboarding-ux.md +49 -0
  115. package/docs/adr/0040-remote-control-default-browser-onramp.md +47 -0
  116. package/docs/adr/0041-temporary-grader-bypass-killswitch.md +40 -0
  117. package/docs/adr/0042-builder-self-deploy-ci-auto-merge.md +82 -0
  118. package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +74 -0
  119. package/docs/adr/0044-mediterranean-palette-replacement.md +73 -0
  120. package/docs/adr/0044-per-box-live-game-preview.md +57 -0
  121. package/docs/adr/0045-devbox-desktop-app.md +133 -0
  122. package/docs/adr/0046-sandbox-first-review-gate.md +72 -0
  123. package/docs/adr/0047-discord-bugs-intake-anti-abuse.md +36 -0
  124. package/docs/adr/0048-works-in-progress-ia-split.md +32 -0
  125. package/docs/adr/0049-split-parallel-safety-contract.md +46 -0
  126. package/docs/adr/0050-device-flow-admission-invite-gated-by-default.md +43 -0
  127. package/docs/adr/0051-full-session-transcript-corpus.md +57 -0
  128. package/docs/adr/0052-sandbox-for-everyone-game-only-preview.md +70 -0
  129. package/docs/adr/0053-scoped-dev-box-session.md +55 -0
  130. package/docs/adr/0054-session-token-reward-cost-plus.md +63 -0
  131. package/docs/adr/0055-server-mediated-branch-publish.md +47 -0
  132. package/docs/adr/0056-prod-deploy-script-mirror-and-healthcheck-retry.md +62 -0
  133. package/docs/adr/0057-container-cost-ledger.md +28 -0
  134. package/docs/adr/0057-discord-archon-approval-channels.md +64 -0
  135. package/docs/adr/0058-automatic-gate-surface-gatekeeper.md +64 -0
  136. package/docs/adr/0059-single-approval-remove-devbox-approval-gate.md +37 -0
  137. package/docs/adr/0060-gds-retrieval-layer.md +97 -0
  138. package/docs/adr/0061-context-layer-decomposition.md +61 -0
  139. package/docs/adr/0062-medusa-instance-model-decoupling.md +107 -0
  140. package/docs/adr/0063-generated-repo-map-symbol-skeleton.md +44 -0
  141. package/docs/adr/0064-rename-medusa-to-cloud-bongos.md +49 -0
  142. package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +69 -0
  143. package/docs/adr/0066-generated-file-map-sections.md +51 -0
  144. package/docs/adr/0067-session-start-fast-forward-pull.md +43 -0
  145. package/docs/adr/0068-shared-art-key-for-newcomers.md +38 -0
  146. package/docs/adr/0069-hall-live-channel-sse-decouple.md +35 -0
  147. package/docs/adr/0070-deterministic-session-card-delivery.md +44 -0
  148. package/docs/adr/0071-box-confirm-before-destroyed-and-drift-reconcile.md +59 -0
  149. package/docs/adr/0072-bongos-app-mac-signed-first-windows-deferred.md +35 -0
  150. package/docs/adr/0072-dev-box-code-staleness-visibility.md +39 -0
  151. package/docs/adr/0073-builder-needs-signal-and-byok-gemini-key.md +64 -0
  152. package/docs/adr/0073-secrets-scan-exclude-uri-detector.md +37 -0
  153. package/docs/adr/0074-memory-map-stays-light.md +32 -0
  154. package/docs/adr/0075-per-system-cost-attribution.md +37 -0
  155. package/docs/adr/0076-in-session-deterministic-compaction.md +58 -0
  156. package/docs/adr/0077-content-addressed-llm-cache.md +32 -0
  157. package/docs/adr/0078-deterministic-first-cron.md +48 -0
  158. package/docs/adr/0079-value-gated-autonomous-engagement.md +39 -0
  159. package/docs/adr/0080-llm-cache-api-transport-and-trust-model.md +46 -0
  160. package/docs/adr/0081-tool-agnostic-design-layer.md +62 -0
  161. package/docs/adr/0082-productized-key-storage-provisioning.md +32 -0
  162. package/docs/adr/0082-server-side-merge-conflict-auto-resolution.md +55 -0
  163. package/docs/adr/0083-modular-architecture-strangler-build-plan.md +143 -0
  164. package/docs/adr/0084-auto-derived-per-task-rank-floor.md +86 -0
  165. package/docs/adr/0085-remove-default-per-builder-spend-cap.md +39 -0
  166. package/docs/adr/0086-goal-scoped-work-hierarchy.md +123 -0
  167. package/docs/adr/0087-bongos-app-architecture-and-handoff-contract.md +49 -0
  168. package/docs/adr/0087-compete-on-governance-not-tooling-cursor.md +65 -0
  169. package/docs/adr/0088-consistent-character-generative-animation.md +169 -0
  170. package/docs/adr/0089-modular-multi-model-adversarial-grader.md +179 -0
  171. package/docs/adr/0090-metic-task-authoring.md +73 -0
  172. package/docs/adr/0091-bounding-the-kernel-and-db-carve.md +106 -0
  173. package/docs/adr/0092-hosting-module-and-on-prem-target.md +117 -0
  174. package/docs/adr/0093-tranche-2-core-carve-sequence.md +72 -0
  175. package/docs/adr/0094-repo-atlas-self-drawing-codebase-map.md +49 -0
  176. package/docs/adr/0095-borrowed-memory-and-retrieval-concepts.md +65 -0
  177. package/docs/adr/0095-cross-agent-context-management.md +58 -0
  178. package/docs/adr/0096-require-reward-before-workable.md +209 -0
  179. package/docs/adr/0097-one-active-claim-per-session-and-worktree-binding.md +46 -0
  180. package/docs/adr/0097-retroactive-reward-backfill.md +124 -0
  181. package/docs/adr/0098-public-mirror-publish-manifest.md +92 -0
  182. package/docs/adr/0099-delayed-redacted-mirror-export.md +55 -0
  183. package/docs/adr/0100-two-repo-instance-model.md +60 -0
  184. package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +90 -0
  185. package/docs/adr/0102-finish-bounding-the-kernel.md +36 -0
  186. package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +70 -0
  187. package/docs/adr/0103-gdsv4-bongos-consolidation.md +28 -0
  188. package/docs/adr/0104-trust-gds-api-channel-in-auto-mode.md +42 -0
  189. package/docs/adr/0105-instance-seed-migrations-out-of-core.md +47 -0
  190. package/docs/adr/0106-goal-owner-manager-authority.md +84 -0
  191. package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +83 -0
  192. package/docs/adr/0108-instance-composition-configurable-root.md +87 -0
  193. package/docs/adr/0109-self-describing-openapi-and-hosted-docs.md +44 -0
  194. package/docs/adr/0110-untrack-whole-file-generated-nav-docs.md +49 -0
  195. package/docs/adr/0111-instance-hosting-provisioning-module.md +135 -0
  196. package/docs/adr/0112-private-goals-invitations-join-requests.md +72 -0
  197. package/docs/adr/0113-sandbox-studio-descope-superseded-by-0044-0052.md +41 -0
  198. package/docs/adr/0114-instance-docs-discovery-contract.md +32 -0
  199. package/docs/adr/0115-scheduled-routines-portability.md +31 -0
  200. package/docs/adr/0116-api-error-envelope-and-code-taxonomy.md +62 -0
  201. package/docs/adr/0117-api-versioning-and-deprecation-policy.md +50 -0
  202. package/docs/adr/0118-api-contract-source-of-truth-code-first.md +47 -0
  203. package/docs/adr/0119-api-pagination-contract.md +34 -0
  204. package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +108 -0
  205. package/docs/adr/0121-greenfield-vs-brownfield-onboarding-adopt-existing-repo.md +97 -0
  206. package/docs/adr/0122-onboarding-diagram-renders-deploy-rendered-off-git.md +43 -0
  207. package/docs/adr/0123-box-idle-sweep-autosave-before-destroy.md +36 -0
  208. package/docs/adr/0124-typed-api-response-bodies.md +37 -0
  209. package/docs/adr/0125-control-plane-scaffolds-standalone-instance-repo.md +36 -0
  210. package/docs/adr/0126-dedicated-cloudbongos-control-plane-droplet.md +30 -0
  211. package/docs/adr/0127-cross-box-fleet-observability.md +64 -0
  212. package/docs/adr/0128-provisioning-runner-privilege-and-db-auth-model.md +46 -0
  213. package/docs/adr/0129-cloudbongos-control-plane-pull-deploy.md +34 -0
  214. package/docs/adr/0130-provision-remote-exec-over-ssh.md +61 -0
  215. package/docs/adr/0131-rank-scoped-skill-visibility.md +47 -0
  216. package/docs/adr/0132-cohost-fleet-control-plane-migration.md +54 -0
  217. package/docs/adr/0133-github-app-manifest-onboarding-signin.md +42 -0
  218. package/docs/adr/0134-private-first-npm-distribution.md +38 -0
  219. package/docs/adr/README.md +208 -0
  220. package/docs/api/openapi.json +13720 -0
  221. package/docs/api-reference.md +437 -0
  222. package/docs/architecture.md +244 -0
  223. package/docs/branding-contract.md +66 -0
  224. package/docs/branding-fill-prompt.md +25 -0
  225. package/docs/canonical-permissions.md +241 -0
  226. package/docs/design/container-exterior-tiles.md +49 -0
  227. package/docs/design/container-exterior.md +81 -0
  228. package/docs/design/container-interior.md +77 -0
  229. package/docs/design/modular-architecture/00-research-report.md +107 -0
  230. package/docs/design/modular-architecture/01-architecture.md +125 -0
  231. package/docs/design/modular-architecture/02-module-map.md +116 -0
  232. package/docs/design/modular-architecture/03-builder-flows.md +104 -0
  233. package/docs/design/modular-architecture/04-module-lifecycle.md +62 -0
  234. package/docs/design/modular-architecture/README.md +27 -0
  235. package/docs/design/vanilla-hall-ui-redesign-scope.md +284 -0
  236. package/docs/design/world-map-scroll.md +41 -0
  237. package/docs/design-contract.md +320 -0
  238. package/docs/file-map.md +418 -0
  239. package/docs/handoff-template.md +92 -0
  240. package/docs/modules-contract.md +293 -0
  241. package/docs/onboarding/browser-terminal-guide.md +73 -0
  242. package/docs/onboarding/diagrams/01-task-lifecycle.mmd +28 -0
  243. package/docs/onboarding/diagrams/02-rank-ladder.mmd +54 -0
  244. package/docs/onboarding/diagrams/03-drachmae-karma.mmd +37 -0
  245. package/docs/onboarding/diagrams/04-architecture.mmd +51 -0
  246. package/docs/onboarding/diagrams/05-versioning.mmd +29 -0
  247. package/docs/onboarding/diagrams/README.md +94 -0
  248. package/docs/onboarding/diagrams/assertions.json +170 -0
  249. package/docs/onboarding/diagrams/puppeteer-config.json +1 -0
  250. package/docs/onboarding/diagrams/render-config.json +9 -0
  251. package/docs/onboarding/drachmae-and-karma.md +178 -0
  252. package/docs/onboarding/primer.md +202 -0
  253. package/docs/project-context.template.md +67 -0
  254. package/docs/recipes/README.md +35 -0
  255. package/docs/recipes/audit-replay.md +100 -0
  256. package/docs/recipes/bongos-cli-release.md +66 -0
  257. package/docs/recipes/builder-box-lifecycle.md +152 -0
  258. package/docs/recipes/claude-design-loop.md +143 -0
  259. package/docs/recipes/claude-gemini-bongos-doors.md +155 -0
  260. package/docs/recipes/cloudbongos-staging-gate.md +139 -0
  261. package/docs/recipes/cloudbongos-standup.md +111 -0
  262. package/docs/recipes/devbox-app-release.md +93 -0
  263. package/docs/recipes/devbox-app-windows-test.md +128 -0
  264. package/docs/recipes/failed-ship-recovery-proof.md +102 -0
  265. package/docs/recipes/figma-design-loop.md +129 -0
  266. package/docs/recipes/gds-db-backup.md +255 -0
  267. package/docs/recipes/instance-deploy.md +37 -0
  268. package/docs/recipes/managed-settings-remote-control.md +416 -0
  269. package/docs/recipes/memory-backup.md +71 -0
  270. package/docs/recipes/memory-sync-conflicts.md +98 -0
  271. package/docs/recipes/multi-builder-merge.md +78 -0
  272. package/docs/recipes/ops-gotchas.md +212 -0
  273. package/docs/recipes/overnight-builder.md +53 -0
  274. package/docs/recipes/packaging-the-core.md +49 -0
  275. package/docs/recipes/parallel-load-results-2026-05-30.md +63 -0
  276. package/docs/recipes/parallel-load-test.md +32 -0
  277. package/docs/recipes/pixel-art-pipeline.md +411 -0
  278. package/docs/recipes/private-npm-distribution.md +79 -0
  279. package/docs/recipes/search-index-ingestion.md +67 -0
  280. package/docs/recipes/self-host.md +60 -0
  281. package/docs/recipes/ship-and-regrade.md +72 -0
  282. package/docs/recipes/staging-data.md +50 -0
  283. package/docs/recipes/staging-environment.md +215 -0
  284. package/docs/recipes/upgrading-the-core.md +58 -0
  285. package/docs/research/cursor-competitive-analysis.md +255 -0
  286. package/docs/research/llm-model-agnostic-and-cheaper-grader.md +227 -0
  287. package/migrations/003_pms.sql +284 -0
  288. package/migrations/004_pms_cost_idempotency.sql +13 -0
  289. package/migrations/005_pms_smoke_filter.sql +44 -0
  290. package/migrations/006_pms_v2.sql +378 -0
  291. package/migrations/007_pms_v2_hierarchy_trigger.sql +59 -0
  292. package/migrations/008_pms_v2_migration_allocator.sql +27 -0
  293. package/migrations/009_pms_v2_blockers_table.sql +68 -0
  294. package/migrations/010_uptime.sql +29 -0
  295. package/migrations/011_backfill_v2_v3_done_when.sql +20 -0
  296. package/migrations/012_pms_v2_task_lifecycle_states.sql +82 -0
  297. package/migrations/013_claim_locking.sql +22 -0
  298. package/migrations/014_pms_task_discipline.sql +86 -0
  299. package/migrations/015_pms_estimation_calibration.sql +140 -0
  300. package/migrations/016_pms_task_grades.sql +60 -0
  301. package/migrations/017_version_progress_exclude_abandoned.sql +49 -0
  302. package/migrations/019_rename_pms_v3_to_gds_v3.sql +65 -0
  303. package/migrations/020_task_dependencies.sql +99 -0
  304. package/migrations/021_v3_renumber_and_deps.sql +224 -0
  305. package/migrations/022_v3_reconcile_dep_gated_status.sql +41 -0
  306. package/migrations/023_pms_rank.sql +30 -0
  307. package/migrations/025_builder_exit_status.sql +15 -0
  308. package/migrations/027_builders_preferred_disciplines.sql +46 -0
  309. package/migrations/028_tasks_parallel_safe.sql +71 -0
  310. package/migrations/029_rank_three_live.sql +32 -0
  311. package/migrations/030_vulnerability_reports.sql +61 -0
  312. package/migrations/031_reward_simplification.sql +57 -0
  313. package/migrations/032_repo_health_snapshots.sql +56 -0
  314. package/migrations/033_audit_log.sql +64 -0
  315. package/migrations/034_lifecycle_breakdown_leaf_only.sql +62 -0
  316. package/migrations/035_auth_token_hygiene.sql +66 -0
  317. package/migrations/037_builder_skill_model_prefs.sql +44 -0
  318. package/migrations/038_task_override_requests.sql +51 -0
  319. package/migrations/039_newcomer_friendly_column.sql +124 -0
  320. package/migrations/040_builder_karma.sql +58 -0
  321. package/migrations/041_kind_multipliers_and_idea_bonus.sql +42 -0
  322. package/migrations/042_criterion_proposal_workflow.sql +78 -0
  323. package/migrations/043_builder_onboarding_observability.sql +66 -0
  324. package/migrations/044_tasks_requires_rank.sql +97 -0
  325. package/migrations/045_builder_onboarding_state.sql +147 -0
  326. package/migrations/046_cost_log_builder_attribution.sql +42 -0
  327. package/migrations/054_builder_memory.sql +108 -0
  328. package/migrations/055_task_criteria.sql +90 -0
  329. package/migrations/058_peer_votes.sql +41 -0
  330. package/migrations/059_onboarding_pick_disciplines.sql +59 -0
  331. package/migrations/060_access_requests.sql +46 -0
  332. package/migrations/061_backfill_override_ship_attribution.sql +29 -0
  333. package/migrations/062_session_records.sql +34 -0
  334. package/migrations/063_session_records_tool_errors_idx.sql +11 -0
  335. package/migrations/064_grade_attempts.sql +18 -0
  336. package/migrations/065_bfg_principal.sql +58 -0
  337. package/migrations/066_live_notify_triggers.sql +69 -0
  338. package/migrations/067_builder_boxes.sql +117 -0
  339. package/migrations/067_discord_bot_foundation.sql +69 -0
  340. package/migrations/068_thetes_requires_rank.sql +27 -0
  341. package/migrations/069_builder_ssh_keys.sql +66 -0
  342. package/migrations/070_box_intents.sql +101 -0
  343. package/migrations/071_box_first_provision_approval.sql +46 -0
  344. package/migrations/072_onboarding_ship_three_remove_primer.sql +80 -0
  345. package/migrations/073_box_terminal_access.sql +46 -0
  346. package/migrations/075_builder_sound_prefs.sql +28 -0
  347. package/migrations/078_box_claude_active.sql +22 -0
  348. package/migrations/079_box_first_connected_at.sql +20 -0
  349. package/migrations/081_tasks_needs_migration.sql +34 -0
  350. package/migrations/084_tasks_security_sensitive.sql +24 -0
  351. package/migrations/085_security_engagement_docs.sql +34 -0
  352. package/migrations/086_rename_disciplines.sql +125 -0
  353. package/migrations/088_builders_anthropic_link.sql +28 -0
  354. package/migrations/089_builders_monthly_budget.sql +19 -0
  355. package/migrations/090_box_scoped_sessions.sql +38 -0
  356. package/migrations/091_session_token_reward.sql +44 -0
  357. package/migrations/093_tasks_sandbox_review.sql +22 -0
  358. package/migrations/102_remove_box_first_provision_approval.sql +58 -0
  359. package/migrations/103_search_chunks.sql +90 -0
  360. package/migrations/104_builder_box_blocked.sql +34 -0
  361. package/migrations/105_box_host_keys.sql +35 -0
  362. package/migrations/111_medusa_customer0_framing.sql +35 -0
  363. package/migrations/112_drop_track_check.sql +25 -0
  364. package/migrations/113_merge_lease.sql +16 -0
  365. package/migrations/114_cloudbongos_rename_live_strings.sql +32 -0
  366. package/migrations/118_currency_label_decoupling.sql +23 -0
  367. package/migrations/119_builder_behavior_prefs.sql +31 -0
  368. package/migrations/121_box_code_version.sql +37 -0
  369. package/migrations/122_builder_gemini_key.sql +30 -0
  370. package/migrations/123_cost_log_tiers.sql +33 -0
  371. package/migrations/124_cost_baseline.sql +45 -0
  372. package/migrations/125_llm_cache.sql +54 -0
  373. package/migrations/126_autonomy_runs.sql +33 -0
  374. package/migrations/143_cost_calibration.sql +63 -0
  375. package/migrations/145_backfill_task_requires_rank.sql +84 -0
  376. package/migrations/160_goal_tier.sql +88 -0
  377. package/migrations/163_polymorphic_dependencies.sql +187 -0
  378. package/migrations/164_event_sound_prefs.sql +20 -0
  379. package/migrations/165_task_module_key.sql +15 -0
  380. package/migrations/171_claims_published_head_sha.sql +24 -0
  381. package/migrations/174_close_orphaned_claims_on_terminal_tasks.sql +30 -0
  382. package/migrations/175_task_grades_diff_hash.sql +9 -0
  383. package/migrations/176_promotion_respects_open_blockers.sql +58 -0
  384. package/migrations/176_ui_discipline.sql +40 -0
  385. package/migrations/177_require_reward_before_workable.sql +76 -0
  386. package/migrations/178_reward_gate_auto_assign_revert_autopromote.sql +61 -0
  387. package/migrations/179_search_chunk_usefulness.sql +33 -0
  388. package/migrations/180_one_active_claim_per_session.sql +54 -0
  389. package/migrations/181_cost_log_skill_name.sql +33 -0
  390. package/migrations/184_claim_release_requested.sql +25 -0
  391. package/migrations/185_module_upstreaming_goal_wiring.sql +59 -0
  392. package/migrations/187_goal_sort_order.sql +30 -0
  393. package/migrations/187_tasks_kind_check_add_verify.sql +21 -0
  394. package/migrations/README.md +88 -0
  395. package/migrations/core_188_goal_visibility_and_membership_requests.sql +71 -0
  396. package/migrations/core_189_core_upgrades_ledger.sql +37 -0
  397. package/migrations/core_189_credit_log_task_id_index.sql +19 -0
  398. package/migrations/core_190_rebuild_claimable_task_views.sql +71 -0
  399. package/migrations/instance/018_pms_v3_planning_seed.sql +131 -0
  400. package/migrations/instance/080_gds_v4_planning_seed.sql +75 -0
  401. package/migrations/instance/110_medusa_planning_seed.sql +70 -0
  402. package/migrations/instance/120_bongos_v1_planning_seed.sql +69 -0
  403. package/migrations/instance/144_bongos_v1_module_system_criterion.sql +26 -0
  404. package/migrations/instance/159_bongos_v1_goal_hierarchy_criterion.sql +29 -0
  405. package/migrations/instance/172_v2_world_planning_seed.sql +163 -0
  406. package/migrations/instance/173_bongos_v1_c7_core_modularized.sql +33 -0
  407. package/migrations/instance/182_gdsv4_bongos_consolidation.sql +132 -0
  408. package/migrations/instance/183_gdsv4_carryover_themed_goals.sql +116 -0
  409. package/migrations/instance/186_bongos_v1_governance_planning_seed.sql +69 -0
  410. package/modules/autonomy/CLAUDE.md +49 -0
  411. package/modules/autonomy/db.js +50 -0
  412. package/modules/autonomy/module.json +13 -0
  413. package/modules/autonomy/pollers/routine-timers.js +146 -0
  414. package/modules/autonomy/routes/autonomy.js +37 -0
  415. package/modules/builder-settings/CLAUDE.md +58 -0
  416. package/modules/builder-settings/builder-needs.js +115 -0
  417. package/modules/builder-settings/db.js +168 -0
  418. package/modules/builder-settings/display-name.js +84 -0
  419. package/modules/builder-settings/event-sound-prefs.js +169 -0
  420. package/modules/builder-settings/interaction-prefs.js +201 -0
  421. package/modules/builder-settings/model-alloc-prefs.js +147 -0
  422. package/modules/builder-settings/module.json +10 -0
  423. package/modules/builder-settings/render-prefs.js +103 -0
  424. package/modules/builder-settings/routes/settings.js +417 -0
  425. package/modules/builder-settings/settings.js +52 -0
  426. package/modules/builder-settings/skill-prefs.js +196 -0
  427. package/modules/builder-settings/sound-prefs.js +178 -0
  428. package/modules/builder-settings/wandering-prefs.js +282 -0
  429. package/modules/dev-box/CLAUDE.md +38 -0
  430. package/modules/dev-box/box-access.js +275 -0
  431. package/modules/dev-box/box-credential.js +94 -0
  432. package/modules/dev-box/box-onboard.js +486 -0
  433. package/modules/dev-box/boxes.js +842 -0
  434. package/modules/dev-box/db.js +39 -0
  435. package/modules/dev-box/module.json +14 -0
  436. package/modules/dev-box/routes/box.js +977 -0
  437. package/modules/discord/CLAUDE.md +50 -0
  438. package/modules/discord/auth.js +54 -0
  439. package/modules/discord/bug-attachments.js +210 -0
  440. package/modules/discord/db.js +96 -0
  441. package/modules/discord/discord-approvals.js +145 -0
  442. package/modules/discord/discord-audit.js +29 -0
  443. package/modules/discord/discord-bot.js +545 -0
  444. package/modules/discord/discord-bugs.js +257 -0
  445. package/modules/discord/discord-channels.js +412 -0
  446. package/modules/discord/discord-inbound.js +147 -0
  447. package/modules/discord/discord-message-buffer.js +54 -0
  448. package/modules/discord/discord-reaction.js +186 -0
  449. package/modules/discord/discord-role-sync.js +318 -0
  450. package/modules/discord/module.json +14 -0
  451. package/modules/discord/pollers/discord-bot.js +19 -0
  452. package/modules/discord/routes/bug-attachments.js +44 -0
  453. package/modules/discord/routes/discord.js +102 -0
  454. package/modules/discord/ship-broadcast.js +78 -0
  455. package/modules/discord/webhook.js +74 -0
  456. package/modules/economy/CLAUDE.md +48 -0
  457. package/modules/economy/achievements.js +594 -0
  458. package/modules/economy/cost-classify.js +331 -0
  459. package/modules/economy/cost-summary-shape.js +90 -0
  460. package/modules/economy/cost.js +438 -0
  461. package/modules/economy/credits.js +430 -0
  462. package/modules/economy/migrations/economy_001_fix_gds_shipper_description.sql +91 -0
  463. package/modules/economy/module.json +10 -0
  464. package/modules/economy/reward.js +61 -0
  465. package/modules/economy/routes/cost.js +97 -0
  466. package/modules/economy/routes/leaderboard.js +26 -0
  467. package/modules/economy/routes/reward.js +42 -0
  468. package/modules/economy/streak.js +285 -0
  469. package/modules/grading/CLAUDE.md +42 -0
  470. package/modules/grading/db.js +39 -0
  471. package/modules/grading/grader-rubric.json +151 -0
  472. package/modules/grading/grader-workers/efficiency.js +213 -0
  473. package/modules/grading/grader-workers/extract-json.js +80 -0
  474. package/modules/grading/grader-workers/fence.js +100 -0
  475. package/modules/grading/grader-workers/hacker.js +239 -0
  476. package/modules/grading/grader-workers/index.js +183 -0
  477. package/modules/grading/grader-workers/narc.js +207 -0
  478. package/modules/grading/grader-workers/quality.js +198 -0
  479. package/modules/grading/grader-workers/rubric.js +20 -0
  480. package/modules/grading/grader-workers/worker-prompt.js +89 -0
  481. package/modules/grading/grader-workers/worker-verdict.js +50 -0
  482. package/modules/grading/grader.js +1790 -0
  483. package/modules/grading/module.json +12 -0
  484. package/modules/grading/routes/grade.js +51 -0
  485. package/modules/hall-ui/CLAUDE.md +61 -0
  486. package/modules/hall-ui/module.json +14 -0
  487. package/modules/hall-ui/public/atlas.html +180 -0
  488. package/modules/hall-ui/public/atlas.js +336 -0
  489. package/modules/hall-ui/public/box-panel.js +423 -0
  490. package/modules/hall-ui/public/brand-apply.js +117 -0
  491. package/modules/hall-ui/public/builders.js +3028 -0
  492. package/modules/hall-ui/public/chime.wav +0 -0
  493. package/modules/hall-ui/public/copy-on-click.js +174 -0
  494. package/modules/hall-ui/public/cursors/arrow.png +0 -0
  495. package/modules/hall-ui/public/cursors/hand-pointer.png +0 -0
  496. package/modules/hall-ui/public/cursors/magnifier-out.png +0 -0
  497. package/modules/hall-ui/public/cursors/magnifier.png +0 -0
  498. package/modules/hall-ui/public/diagram-viewer.css +125 -0
  499. package/modules/hall-ui/public/diagram-viewer.js +330 -0
  500. package/modules/hall-ui/public/diagrams.html +113 -0
  501. package/modules/hall-ui/public/dom-utils.js +212 -0
  502. package/modules/hall-ui/public/drachmae.html +68 -0
  503. package/modules/hall-ui/public/drachmae.js +136 -0
  504. package/modules/hall-ui/public/gate.html +143 -0
  505. package/modules/hall-ui/public/gate.js +228 -0
  506. package/modules/hall-ui/public/goals-page.js +1297 -0
  507. package/modules/hall-ui/public/goals.html +376 -0
  508. package/modules/hall-ui/public/goals.js +119 -0
  509. package/modules/hall-ui/public/hall-widgets.js +189 -0
  510. package/modules/hall-ui/public/harbor.html +167 -0
  511. package/modules/hall-ui/public/harbor.js +335 -0
  512. package/modules/hall-ui/public/index.html +116 -0
  513. package/modules/hall-ui/public/instances-panel.js +172 -0
  514. package/modules/hall-ui/public/md-reader.js +234 -0
  515. package/modules/hall-ui/public/modules.html +119 -0
  516. package/modules/hall-ui/public/modules.js +154 -0
  517. package/modules/hall-ui/public/new-project-wizard.js +186 -0
  518. package/modules/hall-ui/public/not-ready.html +48 -0
  519. package/modules/hall-ui/public/pair.html +119 -0
  520. package/modules/hall-ui/public/pair.js +132 -0
  521. package/modules/hall-ui/public/primer.css +404 -0
  522. package/modules/hall-ui/public/primer.html +85 -0
  523. package/modules/hall-ui/public/primer.js +641 -0
  524. package/modules/hall-ui/public/profile.html +85 -0
  525. package/modules/hall-ui/public/profile.js +236 -0
  526. package/modules/hall-ui/public/ranks.html +69 -0
  527. package/modules/hall-ui/public/ranks.js +206 -0
  528. package/modules/hall-ui/public/roadmap.html +209 -0
  529. package/modules/hall-ui/public/roadmap.js +375 -0
  530. package/modules/hall-ui/public/sessions.html +92 -0
  531. package/modules/hall-ui/public/sessions.js +222 -0
  532. package/modules/hall-ui/public/settings.html +640 -0
  533. package/modules/hall-ui/public/settings.js +1219 -0
  534. package/modules/hall-ui/public/shell.js +495 -0
  535. package/modules/hall-ui/public/style.css +1748 -0
  536. package/modules/hall-ui/public/task-detail.js +253 -0
  537. package/modules/hall-ui/public/task.html +83 -0
  538. package/modules/hall-ui/public/task.js +326 -0
  539. package/modules/hall-ui/public/watch.html +333 -0
  540. package/modules/hall-ui/public/watch.js +859 -0
  541. package/modules/hall-ui/public/work.html +469 -0
  542. package/modules/hall-ui/public/work.js +1292 -0
  543. package/modules/ideas/CLAUDE.md +37 -0
  544. package/modules/ideas/blockers.js +100 -0
  545. package/modules/ideas/inbox.js +459 -0
  546. package/modules/ideas/module.json +10 -0
  547. package/modules/ideas/routes/blockers.js +120 -0
  548. package/modules/ideas/routes/inbox.js +230 -0
  549. package/modules/lifecycle/CLAUDE.md +63 -0
  550. package/modules/lifecycle/conflict-resolve.js +436 -0
  551. package/modules/lifecycle/db.js +4970 -0
  552. package/modules/lifecycle/done-when.js +355 -0
  553. package/modules/lifecycle/github-push.js +969 -0
  554. package/modules/lifecycle/goal-conflicts.js +133 -0
  555. package/modules/lifecycle/hierarchy.js +104 -0
  556. package/modules/lifecycle/lifecycle.js +58 -0
  557. package/modules/lifecycle/merge-lock.js +261 -0
  558. package/modules/lifecycle/migrations/lifecycle_001_goal_first_leads.sql +28 -0
  559. package/modules/lifecycle/migrations/lifecycle_002_backfill_orphan_task_goals.sql +36 -0
  560. package/modules/lifecycle/module.json +13 -0
  561. package/modules/lifecycle/pollers/publish-reconciler.js +20 -0
  562. package/modules/lifecycle/publish-reconciler.js +418 -0
  563. package/modules/lifecycle/routes/analytics.js +63 -0
  564. package/modules/lifecycle/routes/claims.js +506 -0
  565. package/modules/lifecycle/routes/dependencies.js +82 -0
  566. package/modules/lifecycle/routes/done-when.js +136 -0
  567. package/modules/lifecycle/routes/gate-approvals.js +197 -0
  568. package/modules/lifecycle/routes/goals.js +1419 -0
  569. package/modules/lifecycle/routes/lifecycle.js +43 -0
  570. package/modules/lifecycle/routes/tasks.js +1703 -0
  571. package/modules/lifecycle/routes/versions.js +128 -0
  572. package/modules/lifecycle/ship-card.js +505 -0
  573. package/modules/lifecycle/ship-preflight.js +225 -0
  574. package/modules/lifecycle/task-classifier.js +512 -0
  575. package/modules/lifecycle/version-close.js +106 -0
  576. package/modules/memory/CLAUDE.md +40 -0
  577. package/modules/memory/learnings.js +275 -0
  578. package/modules/memory/memory.js +572 -0
  579. package/modules/memory/module.json +12 -0
  580. package/modules/memory/routes/learnings.js +90 -0
  581. package/modules/memory/routes/memory.js +916 -0
  582. package/modules/memory/routes/search.js +188 -0
  583. package/modules/memory/search-chunker.js +252 -0
  584. package/modules/memory/search.js +320 -0
  585. package/modules/onboarding/CLAUDE.md +55 -0
  586. package/modules/onboarding/approval-broadcast.js +59 -0
  587. package/modules/onboarding/db.js +54 -0
  588. package/modules/onboarding/module.json +10 -0
  589. package/modules/onboarding/newcomer-restock.js +212 -0
  590. package/modules/onboarding/onboarding-state.js +532 -0
  591. package/modules/onboarding/onboarding.js +508 -0
  592. package/modules/onboarding/port.js +63 -0
  593. package/modules/onboarding/routes/access-requests.js +208 -0
  594. package/modules/onboarding/routes/onboarding.js +54 -0
  595. package/modules/security/CLAUDE.md +44 -0
  596. package/modules/security/module.json +10 -0
  597. package/modules/security/routes/reports.js +251 -0
  598. package/modules/security/secret-scrub.js +201 -0
  599. package/modules/security/security.js +294 -0
  600. package/modules/sessions/CLAUDE.md +39 -0
  601. package/modules/sessions/bfg.js +357 -0
  602. package/modules/sessions/db.js +326 -0
  603. package/modules/sessions/module.json +10 -0
  604. package/modules/sessions/pulse.js +149 -0
  605. package/modules/sessions/routes/sessions.js +331 -0
  606. package/modules/status-ui/CLAUDE.md +47 -0
  607. package/modules/status-ui/module.json +14 -0
  608. package/modules/status-ui/public/README.md +29 -0
  609. package/modules/status-ui/public/cursors/arrow.png +0 -0
  610. package/modules/status-ui/public/cursors/hand-pointer.png +0 -0
  611. package/modules/status-ui/public/index.html +363 -0
  612. package/modules/status-ui/public/snapshots/.gitkeep +0 -0
  613. package/modules/status-ui/public/status.js +1375 -0
  614. package/modules/status-ui/public/style.css +1342 -0
  615. package/package-lock.json +975 -0
  616. package/package.json +28 -0
  617. package/public-docs/index.html +35 -0
  618. package/public-docs/redoc.standalone.js +1838 -0
  619. package/public-docs/redoc.standalone.js.LICENSE.txt +102 -0
  620. package/public-landing/buddha.png +0 -0
  621. package/public-landing/create.html +868 -0
  622. package/public-landing/index.html +168 -0
  623. package/scripts/bongos-mcp.js +28 -0
  624. package/scripts/cron/style_guide_audit.sh +134 -0
  625. package/scripts/discord/README.md +52 -0
  626. package/scripts/discord/lib.js +80 -0
  627. package/scripts/discord/post.js +51 -0
  628. package/scripts/gds/CLAUDE.md +144 -0
  629. package/scripts/gds/abandon.js +58 -0
  630. package/scripts/gds/adopt-detect.js +123 -0
  631. package/scripts/gds/adopt-import.js +111 -0
  632. package/scripts/gds/adopt-preflight.js +121 -0
  633. package/scripts/gds/api.js +78 -0
  634. package/scripts/gds/architect-audit.js +288 -0
  635. package/scripts/gds/art-stage.js +152 -0
  636. package/scripts/gds/audit-authorship.sh +243 -0
  637. package/scripts/gds/audit-deps.js +121 -0
  638. package/scripts/gds/audit-exports.js +128 -0
  639. package/scripts/gds/audit-replay.sh +161 -0
  640. package/scripts/gds/audit-ships.js +251 -0
  641. package/scripts/gds/autonomy-gate.js +135 -0
  642. package/scripts/gds/backfill-cost.js +165 -0
  643. package/scripts/gds/backfill-sessions.js +351 -0
  644. package/scripts/gds/backfill-task-rewards.js +408 -0
  645. package/scripts/gds/backup.js +94 -0
  646. package/scripts/gds/bfg-principal-token.js +65 -0
  647. package/scripts/gds/box-auth-check.js +160 -0
  648. package/scripts/gds/box-connect-lib.js +453 -0
  649. package/scripts/gds/box.js +1258 -0
  650. package/scripts/gds/build-bongos.js +94 -0
  651. package/scripts/gds/capture.js +123 -0
  652. package/scripts/gds/cf-tunnel.js +419 -0
  653. package/scripts/gds/ci-grade.js +82 -0
  654. package/scripts/gds/claim-recall.js +112 -0
  655. package/scripts/gds/claim.js +333 -0
  656. package/scripts/gds/classify-discipline.js +153 -0
  657. package/scripts/gds/claude-materialize.js +176 -0
  658. package/scripts/gds/cleanup-orphans.js +74 -0
  659. package/scripts/gds/cli-lib.js +518 -0
  660. package/scripts/gds/code.js +101 -0
  661. package/scripts/gds/codemod-rename-src-gds.js +145 -0
  662. package/scripts/gds/conductor-dispatch.js +426 -0
  663. package/scripts/gds/context-pack.js +228 -0
  664. package/scripts/gds/coreB-carve-proof.sh +716 -0
  665. package/scripts/gds/cost.js +47 -0
  666. package/scripts/gds/db-backup-nightly.sh +160 -0
  667. package/scripts/gds/db-backup-offsite.sh +152 -0
  668. package/scripts/gds/demote.js +40 -0
  669. package/scripts/gds/design-sync.js +62 -0
  670. package/scripts/gds/diagnose-achievements.js +118 -0
  671. package/scripts/gds/diagram-facts.js +319 -0
  672. package/scripts/gds/discipline-modes.json +25 -0
  673. package/scripts/gds/discord-channels.js +66 -0
  674. package/scripts/gds/do-api.js +176 -0
  675. package/scripts/gds/docs-entropy.js +403 -0
  676. package/scripts/gds/doctor.js +389 -0
  677. package/scripts/gds/feedback-latest.js +94 -0
  678. package/scripts/gds/fetch-art-key.js +106 -0
  679. package/scripts/gds/fetch-sound-prefs.js +88 -0
  680. package/scripts/gds/figma-design-sync.js +61 -0
  681. package/scripts/gds/fitness.js +1446 -0
  682. package/scripts/gds/gate-review.js +322 -0
  683. package/scripts/gds/gating.js +325 -0
  684. package/scripts/gds/gds-db-backup.service +24 -0
  685. package/scripts/gds/gds-db-backup.timer +20 -0
  686. package/scripts/gds/gds-db-offsite.service +29 -0
  687. package/scripts/gds/gds-db-offsite.timer +18 -0
  688. package/scripts/gds/gen-api-client.js +508 -0
  689. package/scripts/gds/gen-api-docs.js +1037 -0
  690. package/scripts/gds/gen-atlas.js +366 -0
  691. package/scripts/gds/gen-diagrams.js +608 -0
  692. package/scripts/gds/gen-file-map.js +196 -0
  693. package/scripts/gds/gen-repo-map.js +519 -0
  694. package/scripts/gds/gen-session-index.js +210 -0
  695. package/scripts/gds/git-merge-regen.js +147 -0
  696. package/scripts/gds/grade-smoke.js +205 -0
  697. package/scripts/gds/hooks/pre-push.js +169 -0
  698. package/scripts/gds/http-api-client.js +88 -0
  699. package/scripts/gds/init.js +903 -0
  700. package/scripts/gds/install-git-hooks.js +114 -0
  701. package/scripts/gds/key-card.js +213 -0
  702. package/scripts/gds/key-status.js +126 -0
  703. package/scripts/gds/land-watch.js +168 -0
  704. package/scripts/gds/learning-capture.js +145 -0
  705. package/scripts/gds/lib.sh +44 -0
  706. package/scripts/gds/linkify-refs.js +473 -0
  707. package/scripts/gds/local-preview-lib.js +159 -0
  708. package/scripts/gds/local-preview.js +95 -0
  709. package/scripts/gds/login.js +225 -0
  710. package/scripts/gds/main-audit.js +279 -0
  711. package/scripts/gds/me.js +88 -0
  712. package/scripts/gds/memory-map.js +174 -0
  713. package/scripts/gds/merge-lock.js +71 -0
  714. package/scripts/gds/migration-namespace.js +200 -0
  715. package/scripts/gds/mirror-lineage-check.js +319 -0
  716. package/scripts/gds/mirror-redact.js +305 -0
  717. package/scripts/gds/module.js +482 -0
  718. package/scripts/gds/newcomer-floor.config.json +13 -0
  719. package/scripts/gds/newcomer-floor.js +415 -0
  720. package/scripts/gds/newcomer-floor.templates.json +86 -0
  721. package/scripts/gds/newcomer-floor.test.js +235 -0
  722. package/scripts/gds/next-number.js +163 -0
  723. package/scripts/gds/oauth-secret.js +197 -0
  724. package/scripts/gds/onboard.js +217 -0
  725. package/scripts/gds/onboarding-analyze.js +226 -0
  726. package/scripts/gds/onboarding-config.js +178 -0
  727. package/scripts/gds/onboarding-ship.js +92 -0
  728. package/scripts/gds/package-core.js +684 -0
  729. package/scripts/gds/paste-token.js +131 -0
  730. package/scripts/gds/portability-smoke-art.sh +85 -0
  731. package/scripts/gds/portability-smoke-memory.sh +147 -0
  732. package/scripts/gds/provision.js +1324 -0
  733. package/scripts/gds/publish-manifest.js +307 -0
  734. package/scripts/gds/pulse.js +309 -0
  735. package/scripts/gds/push-main.js +45 -0
  736. package/scripts/gds/read-session-export.js +359 -0
  737. package/scripts/gds/reauth.js +149 -0
  738. package/scripts/gds/recall.js +135 -0
  739. package/scripts/gds/release.js +71 -0
  740. package/scripts/gds/render-ideas.js +173 -0
  741. package/scripts/gds/repo-metrics.js +363 -0
  742. package/scripts/gds/resolve-skill-model.js +57 -0
  743. package/scripts/gds/run-routine.js +197 -0
  744. package/scripts/gds/run-unit-tests.js +178 -0
  745. package/scripts/gds/sandbox-stage.js +653 -0
  746. package/scripts/gds/search-ingest-db.js +209 -0
  747. package/scripts/gds/search-ingest-md.js +225 -0
  748. package/scripts/gds/security-baseline-check.sh +180 -0
  749. package/scripts/gds/seed-adopt-tasks.js +75 -0
  750. package/scripts/gds/seed-api-contract-goal-tasks.js +141 -0
  751. package/scripts/gds/seed-bongos-app-tasks.js +294 -0
  752. package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +81 -0
  753. package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +100 -0
  754. package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +307 -0
  755. package/scripts/gds/seed-bongos-module-system-tasks.js +346 -0
  756. package/scripts/gds/seed-bongos-tasks.js +214 -0
  757. package/scripts/gds/seed-gds-v4-tasks.js +624 -0
  758. package/scripts/gds/seed-governance-tasks.js +340 -0
  759. package/scripts/gds/seed-medusa-tasks.js +440 -0
  760. package/scripts/gds/seed-module-upstreaming.js +219 -0
  761. package/scripts/gds/seed-productization-tasks.js +235 -0
  762. package/scripts/gds/seed-provisioning-tasks.js +83 -0
  763. package/scripts/gds/seed-staging.sh +104 -0
  764. package/scripts/gds/seed-v2-world-tasks.js +283 -0
  765. package/scripts/gds/seed-v3-tasks.js +1981 -0
  766. package/scripts/gds/session-cost.js +393 -0
  767. package/scripts/gds/session-digest.js +179 -0
  768. package/scripts/gds/session-eval.js +576 -0
  769. package/scripts/gds/session.js +404 -0
  770. package/scripts/gds/setup.js +504 -0
  771. package/scripts/gds/shell.js +100 -0
  772. package/scripts/gds/ship.js +3618 -0
  773. package/scripts/gds/smoke-batch-claim.sh +213 -0
  774. package/scripts/gds/smoke-batch-release.sh +296 -0
  775. package/scripts/gds/smoke-box.sh +150 -0
  776. package/scripts/gds/smoke-claim-consistency.sh +212 -0
  777. package/scripts/gds/smoke-dependencies.sh +84 -0
  778. package/scripts/gds/smoke-gds.js +125 -0
  779. package/scripts/gds/smoke-write.sh +168 -0
  780. package/scripts/gds/start.js +782 -0
  781. package/scripts/gds/status.js +365 -0
  782. package/scripts/gds/triage.js +366 -0
  783. package/scripts/gds/upgrade.js +421 -0
  784. package/scripts/gds/validate-design.js +254 -0
  785. package/scripts/gds/version-close.js +549 -0
  786. package/scripts/migrate.sh +183 -0
  787. package/scripts/migrate_cost_ledger.sh +178 -0
  788. package/scripts/public-mirror-export.js +291 -0
  789. package/scripts/render-diagrams.sh +88 -0
  790. package/scripts/security/dep-audit.js +276 -0
  791. package/scripts/setup-builder.js +274 -0
  792. package/scripts/setup-builder.sh +28 -0
  793. package/scripts/snapshots/backfill-history.mjs +292 -0
  794. package/scripts/snapshots/maybe-render-on-deploy.sh +66 -0
  795. package/scripts/snapshots/render-pre-art-eras.mjs +329 -0
  796. package/scripts/snapshots/render-world.mjs +322 -0
  797. package/scripts/status-mirror-sync.js +192 -0
  798. package/scripts/status-probe.mjs +185 -0
  799. package/server.js +212 -0
  800. package/src/asset-version.js +71 -0
  801. package/src/bongos/CLAUDE.md +64 -0
  802. package/src/bongos/api-errors.js +281 -0
  803. package/src/bongos/api-path-404.js +67 -0
  804. package/src/bongos/api-prefix.js +38 -0
  805. package/src/bongos/app-pair.js +172 -0
  806. package/src/bongos/auth.js +979 -0
  807. package/src/bongos/cascade-dispatch.js +123 -0
  808. package/src/bongos/db-kernel.js +252 -0
  809. package/src/bongos/db.js +911 -0
  810. package/src/bongos/goal-scope-check.js +120 -0
  811. package/src/bongos/hierarchy-config.js +189 -0
  812. package/src/bongos/live-channel.js +145 -0
  813. package/src/bongos/llm-cache.js +260 -0
  814. package/src/bongos/llm-pricing.js +220 -0
  815. package/src/bongos/metrics.js +286 -0
  816. package/src/bongos/middleware/audit.js +274 -0
  817. package/src/bongos/middleware/error-envelope.js +93 -0
  818. package/src/bongos/middleware/rate-limit.js +195 -0
  819. package/src/bongos/module-scope-map.js +271 -0
  820. package/src/bongos/optimizer.js +166 -0
  821. package/src/bongos/path-match.js +47 -0
  822. package/src/bongos/permission-path-check.js +248 -0
  823. package/src/bongos/pool.js +55 -0
  824. package/src/bongos/repo-info.js +91 -0
  825. package/src/bongos/route-rank-check.js +420 -0
  826. package/src/bongos/routes/CLAUDE.md +43 -0
  827. package/src/bongos/routes/_helpers.js +369 -0
  828. package/src/bongos/routes/audit-log.js +59 -0
  829. package/src/bongos/routes/auth.js +755 -0
  830. package/src/bongos/routes/backup.js +104 -0
  831. package/src/bongos/routes/builders.js +526 -0
  832. package/src/bongos/routes/healthz.js +16 -0
  833. package/src/bongos/routes/instance.js +129 -0
  834. package/src/bongos/routes/live.js +125 -0
  835. package/src/bongos/routes/llm-cache.js +119 -0
  836. package/src/bongos/routes/me.js +380 -0
  837. package/src/bongos/routes/modules.js +131 -0
  838. package/src/bongos/routes/optimizer.js +54 -0
  839. package/src/bongos/routes/public.js +476 -0
  840. package/src/bongos/routes/security.js +207 -0
  841. package/src/bongos/routes.js +166 -0
  842. package/src/bongos/secret-box.js +177 -0
  843. package/src/bongos/sensitive-surfaces.js +30 -0
  844. package/src/bongos/serve-internal.js +889 -0
  845. package/src/bongos/uptime-poller.js +90 -0
  846. package/src/bongos-downloads.js +70 -0
  847. package/src/branding.js +182 -0
  848. package/src/build-info.js +77 -0
  849. package/src/devbox-downloads.js +67 -0
  850. package/src/instance-config.js +200 -0
  851. package/src/module-api.js +366 -0
  852. package/src/module-loader/loader.js +268 -0
  853. package/src/module-loader/manifest-schema.js +140 -0
  854. package/src/module-loader/provenance.js +28 -0
  855. package/src/module-loader/semver.js +100 -0
  856. package/src/module-seams.js +291 -0
  857. package/src/modules.js +254 -0
  858. package/src/platform-server.js +135 -0
  859. package/src/ui/adapters/claude-design/index.js +333 -0
  860. package/src/ui/adapters/figma/index.js +328 -0
  861. package/tests/abandon_task.mjs +138 -0
  862. package/tests/achievements_reconcile.mjs +167 -0
  863. package/tests/adopt_detect.mjs +141 -0
  864. package/tests/adopt_import.mjs +132 -0
  865. package/tests/adopt_preflight.mjs +105 -0
  866. package/tests/anti_grid.mjs +89 -0
  867. package/tests/api_client.mjs +231 -0
  868. package/tests/api_docs.mjs +283 -0
  869. package/tests/api_error_envelope_buildcheck.mjs +56 -0
  870. package/tests/api_error_envelope_middleware.mjs +99 -0
  871. package/tests/api_error_registry.mjs +108 -0
  872. package/tests/api_pagination_contract.mjs +51 -0
  873. package/tests/api_path_404.mjs +96 -0
  874. package/tests/app_pair.mjs +168 -0
  875. package/tests/architect.mjs +140 -0
  876. package/tests/art_key.mjs +121 -0
  877. package/tests/art_stage.mjs +121 -0
  878. package/tests/asset_version.mjs +94 -0
  879. package/tests/atlas.mjs +69 -0
  880. package/tests/audit_deps.mjs +74 -0
  881. package/tests/audit_middleware.mjs +385 -0
  882. package/tests/auth_admission_gate.mjs +164 -0
  883. package/tests/auth_anthropic_identity.mjs +95 -0
  884. package/tests/auth_duplicate_cookie.mjs +56 -0
  885. package/tests/auth_error_brand.mjs +77 -0
  886. package/tests/auth_flow_slice1.mjs +223 -0
  887. package/tests/auth_inactive_builder.mjs +94 -0
  888. package/tests/auth_page_gate.mjs +92 -0
  889. package/tests/auth_session_ttl.mjs +155 -0
  890. package/tests/autonomous_fit_protected_paths.mjs +115 -0
  891. package/tests/autonomy_gate.mjs +85 -0
  892. package/tests/autotile_variants.mjs +256 -0
  893. package/tests/autotile_xverify.mjs +133 -0
  894. package/tests/backfill_sessions.mjs +101 -0
  895. package/tests/backfill_task_rewards.mjs +152 -0
  896. package/tests/backup_cli.mjs +104 -0
  897. package/tests/bfg.mjs +327 -0
  898. package/tests/bfg_write.mjs +423 -0
  899. package/tests/bongos_box_connect.mjs +89 -0
  900. package/tests/bongos_dispatch.mjs +74 -0
  901. package/tests/bongos_downloads.mjs +118 -0
  902. package/tests/bongos_login.mjs +34 -0
  903. package/tests/box_access.mjs +186 -0
  904. package/tests/box_auth_check.mjs +114 -0
  905. package/tests/box_code_staleness.mjs +116 -0
  906. package/tests/box_connect_e2e.mjs +196 -0
  907. package/tests/box_cost.mjs +70 -0
  908. package/tests/box_dns_repoint.mjs +151 -0
  909. package/tests/box_host_keys.mjs +102 -0
  910. package/tests/box_onboard.mjs +317 -0
  911. package/tests/box_scope_session.mjs +115 -0
  912. package/tests/box_ship_permissions.mjs +71 -0
  913. package/tests/box_terminal_ssrf.mjs +90 -0
  914. package/tests/boxes.mjs +1255 -0
  915. package/tests/branding.mjs +88 -0
  916. package/tests/bug_attachments.mjs +189 -0
  917. package/tests/build_info.mjs +88 -0
  918. package/tests/builder_needs.mjs +99 -0
  919. package/tests/cap_newcomer_tasks.mjs +114 -0
  920. package/tests/cascade-dispatch.mjs +95 -0
  921. package/tests/cf_tunnel.mjs +459 -0
  922. package/tests/claim_recall.mjs +150 -0
  923. package/tests/claims_bind_session.mjs +47 -0
  924. package/tests/claude_materialize.mjs +148 -0
  925. package/tests/cli_box_scope.mjs +52 -0
  926. package/tests/cli_lib_formatters.mjs +87 -0
  927. package/tests/cli_token_reissue.mjs +147 -0
  928. package/tests/client_bundle_route.mjs +47 -0
  929. package/tests/cloud_bongos_pack.mjs +85 -0
  930. package/tests/conductor.mjs +496 -0
  931. package/tests/confirm_attribution.mjs +156 -0
  932. package/tests/conflict_resolve.mjs +462 -0
  933. package/tests/context_pack.mjs +240 -0
  934. package/tests/cost.mjs +94 -0
  935. package/tests/cost_audit.mjs +122 -0
  936. package/tests/cost_budget_status.mjs +78 -0
  937. package/tests/cost_reconcile.mjs +102 -0
  938. package/tests/cost_summary_shape.mjs +94 -0
  939. package/tests/credit_grant.mjs +376 -0
  940. package/tests/criterion_progress.mjs +220 -0
  941. package/tests/criterion_proposal.mjs +109 -0
  942. package/tests/criterion_ref_parse.mjs +53 -0
  943. package/tests/currency_label.mjs +119 -0
  944. package/tests/day_cycle.mjs +197 -0
  945. package/tests/dependencies_migration.mjs +77 -0
  946. package/tests/dependencies_polymorphic.mjs +212 -0
  947. package/tests/deploy_instance.mjs +78 -0
  948. package/tests/devbox_downloads.mjs +122 -0
  949. package/tests/diff_derived_signals.mjs +115 -0
  950. package/tests/discord_approvals.mjs +174 -0
  951. package/tests/discord_autojoin.mjs +158 -0
  952. package/tests/discord_bot.mjs +94 -0
  953. package/tests/discord_bugs.mjs +297 -0
  954. package/tests/discord_channels.mjs +337 -0
  955. package/tests/discord_foundation.mjs +76 -0
  956. package/tests/discord_inbound.mjs +190 -0
  957. package/tests/discord_message_buffer.mjs +87 -0
  958. package/tests/discord_oauth.mjs +55 -0
  959. package/tests/discord_reaction.mjs +170 -0
  960. package/tests/discord_role_sync.mjs +151 -0
  961. package/tests/display_name.mjs +123 -0
  962. package/tests/dns_preflight.mjs +74 -0
  963. package/tests/event_sound_prefs.mjs +194 -0
  964. package/tests/eviction_policy.mjs +279 -0
  965. package/tests/figma_adapter.mjs +199 -0
  966. package/tests/file_map.mjs +91 -0
  967. package/tests/first_admin_bootstrap.mjs +40 -0
  968. package/tests/fitness.mjs +435 -0
  969. package/tests/fitness_two_root.mjs +104 -0
  970. package/tests/fixtures/session-506-digest.json +34 -0
  971. package/tests/gate_approvals.mjs +451 -0
  972. package/tests/gate_review.mjs +152 -0
  973. package/tests/git_merge_regen.mjs +151 -0
  974. package/tests/github_push_ancestry.mjs +119 -0
  975. package/tests/github_push_land_proof.mjs +127 -0
  976. package/tests/github_push_merge_driver.mjs +175 -0
  977. package/tests/goal_abuse_matrix.mjs +187 -0
  978. package/tests/goal_achievement.mjs +146 -0
  979. package/tests/goal_conflicts.mjs +305 -0
  980. package/tests/goal_dependencies_read.mjs +73 -0
  981. package/tests/goal_member_roles.mjs +420 -0
  982. package/tests/goal_membership_requests.mjs +332 -0
  983. package/tests/goal_routes.mjs +203 -0
  984. package/tests/goal_scope_check.mjs +167 -0
  985. package/tests/goal_tier.mjs +312 -0
  986. package/tests/goal_wall_proof.mjs +112 -0
  987. package/tests/grade_attempts.mjs +73 -0
  988. package/tests/grade_attribution.mjs +117 -0
  989. package/tests/grade_diff_hash.mjs +191 -0
  990. package/tests/grade_server_authoritative.mjs +305 -0
  991. package/tests/grade_unavailable_display.mjs +66 -0
  992. package/tests/grader.mjs +1960 -0
  993. package/tests/grader_bypass.mjs +57 -0
  994. package/tests/grader_panel.mjs +252 -0
  995. package/tests/grader_prompt_fencing.mjs +192 -0
  996. package/tests/helpers.mjs +52 -0
  997. package/tests/hierarchy-config.mjs +81 -0
  998. package/tests/init.mjs +463 -0
  999. package/tests/instance_config.mjs +76 -0
  1000. package/tests/instance_manifest.mjs +110 -0
  1001. package/tests/interaction_prefs.mjs +72 -0
  1002. package/tests/key-card.mjs +110 -0
  1003. package/tests/key-status.mjs +45 -0
  1004. package/tests/land_watch.mjs +115 -0
  1005. package/tests/lifecycle-card.mjs +145 -0
  1006. package/tests/lifecycle_breakdown_view.mjs +236 -0
  1007. package/tests/linkify_refs.mjs +264 -0
  1008. package/tests/live_channel.mjs +128 -0
  1009. package/tests/live_sse.mjs +122 -0
  1010. package/tests/llm-cache-route.mjs +110 -0
  1011. package/tests/llm-cache.mjs +140 -0
  1012. package/tests/llm-pricing.mjs +152 -0
  1013. package/tests/local_preview.mjs +136 -0
  1014. package/tests/main_audit.mjs +137 -0
  1015. package/tests/memory_isolation.mjs +431 -0
  1016. package/tests/memory_map.mjs +107 -0
  1017. package/tests/memory_pull.mjs +429 -0
  1018. package/tests/merge_lease.mjs +100 -0
  1019. package/tests/merge_lock.mjs +312 -0
  1020. package/tests/metrics.mjs +147 -0
  1021. package/tests/migration_allocation.mjs +146 -0
  1022. package/tests/migration_namespace.mjs +185 -0
  1023. package/tests/migrations_fresh_replay_guard.mjs +38 -0
  1024. package/tests/mirror_lineage_fulltree.mjs +45 -0
  1025. package/tests/mirror_lineage_proof.mjs +72 -0
  1026. package/tests/mirror_redact.mjs +193 -0
  1027. package/tests/model_alloc_prefs.mjs +63 -0
  1028. package/tests/module-scope-map.mjs +88 -0
  1029. package/tests/module_api.mjs +110 -0
  1030. package/tests/module_cli.mjs +292 -0
  1031. package/tests/module_contributions.mjs +148 -0
  1032. package/tests/module_loader.mjs +286 -0
  1033. package/tests/module_manifest.mjs +194 -0
  1034. package/tests/module_owned_tests.mjs +105 -0
  1035. package/tests/module_provenance.mjs +34 -0
  1036. package/tests/module_route_rank.mjs +147 -0
  1037. package/tests/module_seams.mjs +68 -0
  1038. package/tests/modules.mjs +156 -0
  1039. package/tests/modules_route.mjs +38 -0
  1040. package/tests/newcomer_floor.mjs +17 -0
  1041. package/tests/newcomer_restock.mjs +80 -0
  1042. package/tests/next_number.mjs +154 -0
  1043. package/tests/oauth_secret.mjs +60 -0
  1044. package/tests/onboard.mjs +70 -0
  1045. package/tests/onboarding_gate.mjs +267 -0
  1046. package/tests/onboarding_observability.mjs +344 -0
  1047. package/tests/onboarding_route_signals.mjs +175 -0
  1048. package/tests/onboarding_state.mjs +346 -0
  1049. package/tests/one_active_claim_per_session.mjs +111 -0
  1050. package/tests/orphaned_claim_ship.mjs +135 -0
  1051. package/tests/override_attribution.mjs +142 -0
  1052. package/tests/override_request_ownership.mjs +235 -0
  1053. package/tests/package_core.mjs +435 -0
  1054. package/tests/parallel-load.mjs +402 -0
  1055. package/tests/path_match.mjs +62 -0
  1056. package/tests/permission_path.mjs +151 -0
  1057. package/tests/platform_boot.mjs +167 -0
  1058. package/tests/pre_compact.mjs +60 -0
  1059. package/tests/preview_server.mjs +127 -0
  1060. package/tests/profile_route.mjs +197 -0
  1061. package/tests/provision.mjs +805 -0
  1062. package/tests/provisioning_github_app_routes.mjs +98 -0
  1063. package/tests/public_mirror_export.mjs +124 -0
  1064. package/tests/public_task_resolver.mjs +112 -0
  1065. package/tests/publish_branch_route.mjs +422 -0
  1066. package/tests/publish_manifest.mjs +184 -0
  1067. package/tests/publish_reconciler.mjs +728 -0
  1068. package/tests/rank_gate.mjs +463 -0
  1069. package/tests/rate_limit_public.mjs +217 -0
  1070. package/tests/read_session_export.mjs +89 -0
  1071. package/tests/recall_cli.mjs +82 -0
  1072. package/tests/recall_hints.mjs +119 -0
  1073. package/tests/recent_shipped_window.mjs +80 -0
  1074. package/tests/regrade_eligibility.mjs +202 -0
  1075. package/tests/render_prefs.mjs +122 -0
  1076. package/tests/repo_map.mjs +92 -0
  1077. package/tests/reward_gate.mjs +284 -0
  1078. package/tests/route_rank_check.mjs +243 -0
  1079. package/tests/route_validation.mjs +490 -0
  1080. package/tests/routine_timers.mjs +72 -0
  1081. package/tests/rubric_fixtures.mjs +140 -0
  1082. package/tests/run_routine.mjs +69 -0
  1083. package/tests/sandbox_stage.mjs +582 -0
  1084. package/tests/scrubber_corpus.mjs +257 -0
  1085. package/tests/search_chunker.mjs +209 -0
  1086. package/tests/search_ingest.mjs +96 -0
  1087. package/tests/search_ingest_db.mjs +88 -0
  1088. package/tests/search_isolation.mjs +204 -0
  1089. package/tests/search_route.mjs +95 -0
  1090. package/tests/secret_box.mjs +189 -0
  1091. package/tests/security_docs.mjs +186 -0
  1092. package/tests/security_route.mjs +108 -0
  1093. package/tests/served_paths_shipped.mjs +118 -0
  1094. package/tests/session-cards.mjs +142 -0
  1095. package/tests/session_506_regression.mjs +69 -0
  1096. package/tests/session_caps.mjs +238 -0
  1097. package/tests/session_cost.mjs +133 -0
  1098. package/tests/session_digest.mjs +73 -0
  1099. package/tests/session_eval.mjs +386 -0
  1100. package/tests/session_index.mjs +105 -0
  1101. package/tests/session_records.mjs +369 -0
  1102. package/tests/session_search.mjs +78 -0
  1103. package/tests/session_start_freshness.mjs +123 -0
  1104. package/tests/setup_smoke.mjs +154 -0
  1105. package/tests/ship_autolinkify.mjs +60 -0
  1106. package/tests/ship_automerge_injection.mjs +174 -0
  1107. package/tests/ship_broadcast_routing.mjs +125 -0
  1108. package/tests/ship_broadcast_server.mjs +175 -0
  1109. package/tests/ship_card.mjs +280 -0
  1110. package/tests/ship_ci_deploy.mjs +374 -0
  1111. package/tests/ship_preflight.mjs +368 -0
  1112. package/tests/ship_premerge.mjs +75 -0
  1113. package/tests/ship_resume.mjs +129 -0
  1114. package/tests/ship_upload_skip.mjs +89 -0
  1115. package/tests/ship_zero_commit.mjs +60 -0
  1116. package/tests/single_host_hall.mjs +98 -0
  1117. package/tests/skill_prefs.mjs +150 -0
  1118. package/tests/smoke_preflight.mjs +66 -0
  1119. package/tests/snapshot_picker.mjs +233 -0
  1120. package/tests/sound_prefs.mjs +198 -0
  1121. package/tests/sprite_facing.mjs +35 -0
  1122. package/tests/status-probe.mjs +89 -0
  1123. package/tests/status_backfill.mjs +94 -0
  1124. package/tests/stop_refcheck.mjs +127 -0
  1125. package/tests/streak.mjs +175 -0
  1126. package/tests/subdomain_auth.mjs +58 -0
  1127. package/tests/task_classifier_vocab.mjs +41 -0
  1128. package/tests/task_create_idempotency.mjs +96 -0
  1129. package/tests/task_goal_link.mjs +93 -0
  1130. package/tests/task_ownership_gate.mjs +125 -0
  1131. package/tests/terrain_picker.mjs +298 -0
  1132. package/tests/touches_overlap.mjs +75 -0
  1133. package/tests/triage_development.mjs +140 -0
  1134. package/tests/turn_in_place.mjs +280 -0
  1135. package/tests/upgrade.mjs +357 -0
  1136. package/tests/verify_task_guard.mjs +64 -0
  1137. package/tests/version_close.mjs +76 -0
  1138. package/tests/versions_route.mjs +93 -0
  1139. package/tests/walkability.mjs +188 -0
  1140. package/tests/wandering_prefs.mjs +176 -0
  1141. package/tests/worktree_disambiguation.mjs +105 -0
  1142. package/tests/worktree_guard.mjs +90 -0
@@ -0,0 +1,82 @@
1
+ # ADR 0033 — Discord bot as a service principal (account-linking, both-mode upvoting, role-sync, inbound retirement)
2
+
3
+ **Date:** 2026-06-04
4
+ **Context:** GDS-V3, task [#619](https://example.com/builders#/task/619). Phase 3 of the Discord integration begun in [ADR 0032](0032-discord-mirror-of-gds.md) ([#607](https://example.com/builders#/task/607)). Where Phase 2 was a one-way webhook (no identity, no inbound), Phase 3 adds a **bot** — a persistent connection that reads reactions/messages and assigns roles — which means Discord *actions* now trigger GDS *writes*. That lands on the trust boundary. Companion to [ADR 0016](<redacted>.md) (the boundary), [ADR 0026](<redacted>.md) (the service-principal + identity-gate pattern this reuses), [ADR 0018](<redacted>.md) (the ranks role-sync mirrors), [ADR 0022](0022-secrets-policy.md) (bot token + OAuth secret), and [ADR 0008](0008-google-chat-oauth-user-auth.md) (the inbound path this retires).
5
+ **Status:** Accepted (design). Implementation seeded as separate sub-tasks (listed at the end); nothing here is built yet. Foundation build is blocked on creating the Discord bot application.
6
+ **Track:** `internal` (dev-system / team communication).
7
+
8
+ > A decision record, not an implementation. The point is to lock the shape — especially the authorization model — so the build can proceed in reviewable pieces without relitigating the trust question each time.
9
+
10
+ ## Problem
11
+
12
+ ADR 0032 established that Discord is a one-way mirror of the GDS. Phase 2 honored that trivially: a webhook only ever posts *out*. Phase 3's features all push the other way:
13
+
14
+ - **Upvoting** — a reaction in Discord should feed the vote/karma system.
15
+ - **Role-sync** — builder ranks should appear as Discord role badges.
16
+ - **Inbound** — team messages in Discord should become ideas, retiring the Google Chat inbound path.
17
+
18
+ The first and third make a **Discord event cause a GDS write**. Naively, a bot with a database connection could just write karma or votes. That would re-open exactly the hole [ADR 0016](<redacted>.md) closes: an actor *outside* the trust boundary deciding authoritative state. We need a design where the bot can act **without ever becoming an authority**.
19
+
20
+ ## Decision
21
+
22
+ ### 1. The bot is a service principal — trusted for identity, never for authorization
23
+
24
+ The bot authenticates to the GDS as a dedicated **service principal** (`system_role = 'discord_bot'`), gated by a `requireDiscordBot` middleware that checks that exact identity — **not** a rank. This is the same shape the BFG cross-builder write path uses ([ADR 0026 §6C](<redacted>.md)): a stolen or rank-escalated *human* token has `system_role = NULL` and is rejected; only the bot principal's row passes.
25
+
26
+ The principal is trusted for **one** thing: asserting "this Discord user, who reacted/posted, is linked builder X." It is **never** trusted to decide what X may do. Every write the bot triggers re-derives authorization from **X's live `builders.rank`** server-side, through the same guards the web/CLI paths use. The bot cannot escalate; at most it can mis-report *which linked user* acted, and that blast radius is bounded (below).
27
+
28
+ Every bot-triggered write is recorded in `audit_log`, tagged with the originating surface (`discord_bot`) and the asserted Discord user id, so bot-originated peer-votes are distinguishable from direct web/CLI votes during forensic review. (The `audit_log` cited as a control below must carry this surface field — F1 adds it.)
29
+
30
+ ### 2. Account-linking is UI-first and self-asserted
31
+
32
+ A Discord user becomes "trusted as builder X" only when **X personally links it**: signed into the `/builders` hall (already GitHub-authed), X clicks **Link Discord**, approves a standard Discord OAuth2 sign-in (`identify` scope), and the server stores the `(builder_id ↔ discord_user_id)` pair in a new `discord_links` table. **CSRF protection is mandatory:** the authorize request carries a one-time, unguessable **`state`** parameter bound to X's session (the same protection the existing GitHub web flow uses — `buildAuthorizeUrl(state, …)` in `auth.js`), and `/discord/callback` **rejects any request whose `state` doesn't match**. Without it, an attacker could trick a logged-in builder's browser into a forged callback carrying an attacker-controlled `code`, silently linking the *attacker's* Discord account to the *victim's* builder row — after which every attacker reaction would cast peer-votes as the victim. The `state` check closes that vector; it is a hard requirement of F2, not an option. An **Unlink** affordance is always present. No tokens, no terminal — per the project's <redacted> rule. Because the mapping comes from the *user's own OAuth*, not from the bot, the bot is not trusted to *invent* mappings — only to report which (already-linked) user fired an event.
33
+
34
+ ### 3. Upvoting — both modes, cleanly separated
35
+
36
+ A 👍 on a shipped item in **#ship-feed** flows: bot → `requireDiscordBot` endpoint with `{discord_user_id, target_task_id}`. The server resolves the link and branches:
37
+
38
+ - **Linked builder is Metic+ and not the author** → cast a **real peer-vote** through the *existing* `peer_votes` path, re-enforcing every guard server-side: Metic+ only, anti-self-vote, the 7-day karma lag, the tallied-freeze. (Identical authorization to `POST /tasks/:id/vote`.)
39
+ - **Anyone else** (player, Xenos, unlinked, or self-vote) → record a **cosmetic applause** in a separate `discord_applause` store that **never touches `karma_log`**.
40
+
41
+ The two stores are disjoint by construction: applause is engagement flavor; karma is earned only through the rank-gated path. A Xenos's enthusiasm shows as applause and changes no reputation.
42
+
43
+ ### 4. Role-sync is one-way (GDS → Discord)
44
+
45
+ On rank change, on link, and on a periodic reconcile, the bot sets the member's Discord role (Xenos / Metic / Archon) from `builders.rank`. This is read-only against the GDS and write-only against Discord — it can never feed authority back in. It makes the role badges *honest* (the point of ADR 0032 §2). Requires the bot to hold **Manage Roles** and to sit above the managed roles in Discord's hierarchy.
46
+
47
+ ### 5. Inbound retirement
48
+
49
+ The bot listens on a designated channel, files messages into `idea_inbox` via the existing file-idea path (attributed to the linked builder when known), and then the Google Chat **inbound** machinery is retired: the `SessionStart` `poll.js` hook, the `chat-intake-daily` routine, and the `chat-inbox.md` flow. (`scripts/chat/` can then be deleted entirely — outbound already moved in [#607](https://example.com/builders#/task/607).) Requires the **Message Content** intent.
50
+
51
+ ### Hosting & secrets
52
+
53
+ The bot runs **in-process** in the existing Node server as a module, behind a `DISCORD_BOT_ENABLED` kill-switch (default **off**, fail-closed — like `BFG_WRITE_ENABLED`), with reconnect handling. Adds the `discord.js` dependency. Cost is **~$0** (it rides the droplet we already pay for; Discord is free).
54
+
55
+ **Secret-deployment note (differs from Phase 2):** the Phase 2 webhook ran on the operator's laptop during `/builder-ship`, so its secret stayed local. The bot runs **on the droplet** (a persistent gateway connection), so the **bot token + Discord OAuth client secret must live on the droplet** (systemd `EnvironmentFile`), per [ADR 0022](0022-secrets-policy.md) — not only in `~/.config/otb/`. This is the first Discord secret the droplet needs.
56
+
57
+ ## Consequences
58
+
59
+ - The trust boundary holds: the bot maps identity; the GDS still decides. The one invariant to defend in review is **"every bot-triggered write re-checks the linked builder's live rank."**
60
+ - Discord role badges become continuously honest (auto-synced from the DB).
61
+ - Google Chat is fully retired once inbound lands; `scripts/chat/` is removed then.
62
+ - New surface area to own: a `discord.js` dependency, an always-on connection (reconnect/rate-limit handling), and a droplet-side secret.
63
+ - A compromised bot could forge "user X reacted," yielding at most a spurious Metic+ peer-vote — bounded by the anti-self-vote guard, the visible 7-day karma lag, and the `audit_log`, and revocable by the `DISCORD_BOT_ENABLED` kill-switch.
64
+
65
+ ## Alternatives considered
66
+
67
+ - **Bot writes karma/votes directly (its own DB access).** Rejected — it makes the bot an authority and re-opens [ADR 0016](<redacted>.md). The principal + per-write rank re-check is the whole point.
68
+ - **Trust Discord roles for authorization.** Rejected outright (ADR 0016 / 0032 §2): roles are cosmetic reflections, never keys.
69
+ - **Map Discord→builder by matching usernames (no OAuth link).** Rejected — unverified and spoofable; identity must be self-asserted by the builder.
70
+ - **A separate bot process / service.** Considered; in-process behind a kill-switch is simpler for one droplet. Revisit if the connection destabilizes the web process.
71
+ - **Applause feeds karma (single mode).** Rejected — it would let unranked enthusiasm mint reputation. Keeping applause cosmetic and disjoint from `karma_log` is the load-bearing separation of decision [#3](https://example.com/builders#/task/3).
72
+
73
+ ## Implementation sub-tasks (seeded separately)
74
+
75
+ 1. **F1 — Foundation** (no live bot needed): a new migration (next free number at build time) adding `discord_links`, `discord_applause`, the `discord_bot` service principal, and the `audit_log` originating-surface field + `requireDiscordBot` gate + db helpers + unit tests.
76
+ 2. **F2 — Account-linking web flow** (needs OAuth creds): Discord OAuth start/callback routes + Link/Unlink UI on the hall.
77
+ 3. **F3 — Bot process** (needs bot token): `discord.js` client, `DISCORD_BOT_ENABLED`-gated, droplet EnvironmentFile, connection + reconnect.
78
+ 4. **F4 — Upvoting** (needs F1–F3): reaction handler → bot-principal endpoint (real peer-vote if Metic+, else applause).
79
+ 5. **F5 — Role-sync** (needs F1–F3 + Manage Roles): rank → Discord role on change/link/reconcile.
80
+ 6. **F6 — Inbound** (needs F3): message handler → `idea_inbox`; retire Google Chat inbound + delete `scripts/chat/`.
81
+
82
+ **Prerequisite (human, browser):** create the Discord bot application — bot token, OAuth2 client id/secret, the Server Members + Message Content + Guild Message Reactions intents, and the Manage Roles permission. Gates F2/F3 onward.
@@ -0,0 +1,53 @@
1
+ # ADR 0034 — `thetes`: a 4th live rank for graduated newcomers, with auto-graduation after 3 ships
2
+
3
+ **Date:** 2026-06-05
4
+ **Context:** GDS-V3, task [#692](https://example.com/builders#/task/692). Amends [ADR 0018](<redacted>.md) (the three-rank model). Companion to [ADR 0016](<redacted>.md) (server-enforced permissions) and the newcomer-supply work (the `newcomer-generator` routine). Role-sync mirrors it to Discord per [ADR 0033](<redacted>.md).
5
+ **Status:** Accepted + shipped.
6
+ **Track:** `internal` (dev-system / builder onboarding).
7
+
8
+ ## Problem
9
+
10
+ A new builder defaults to `xenos`, the sandboxed entry rank, which can claim **only** tasks flagged `newcomer_friendly`. That pool is chronically under-supplied: a fresh Xenos repeatedly arrives to an empty queue, files some ideas (which a Xenos cannot even promote — `PATCH /inbox` is Metic+), and finds the queue still empty. Builder 25 hit exactly this loop. The supply-side fix (a generator that tops up `newcomer_friendly` tasks) helps, but it fights physics: it must perpetually feed *every* newcomer's *entire* tenure.
11
+
12
+ The jump out of the sandbox was also all-or-nothing: `xenos` → `metic`, where Metic carries real trust (idea triage, blocker resolution, red-team filing, planning/priority, claiming sensitive infra). That is too big a grant to hand out just so someone can pick up ordinary work, so it stayed gated behind a manual Archon decision — leaving newcomers stuck in the starved sandbox in the meantime.
13
+
14
+ ## Decision
15
+
16
+ Add **`thetes`** as a 4th LIVE rank between `xenos` and `metic`, and **time-box the sandbox**: a Xenos auto-graduates to Thetes after shipping a few tasks.
17
+
18
+ ### 1. The ladder gains a tier
19
+
20
+ `xenos (1) < thetes (2) < metic (3) < archon (4)` — the `REQUIRES_RANK_TIER` map in `src/bongos/db.js`. `thetes` was already a reserved value in the `builders.rank` CHECK enum (migration 023), so no `builders` schema change was needed; migration `068` only widens the inverse gate `tasks.requires_rank` to accept `'thetes'`.
21
+
22
+ ### 2. What Thetes can and cannot do
23
+
24
+ - **Can:** claim the **entire general queue** — any task whose `requires_rank` is the default `'xenos'` — with **no** `newcomer_friendly` restriction. This is the whole point: a Thetes draws from the large pool, not the starved newcomer pool.
25
+ - **Cannot:** anything scope-shaping or trust-bearing. It is blocked from tasks raised to `requires_rank='metic'/'archon'`, and — because `requireRank()` is **exact-set membership** (`src/bongos/auth.js`), not tier comparison — it inherits **none** of Metic's route grants (idea triage, blocker resolution, red-team, planning/priority, task create/edit). Those stay Metic+ with zero code change.
26
+
27
+ This asymmetry is what makes the next decision safe.
28
+
29
+ ### 3. Auto-graduation is automatic, bounded, and low-risk
30
+
31
+ A Xenos is promoted to Thetes the moment their **3rd** task reaches `shipped` (merged + deployed). It happens server-side, **inside the ship transaction** (`db.shipTask` → `db.maybeAutoGraduateToThetes`), atomic with the ship. The count is `COUNT(*)` of the builder's tasks at `status='shipped'` **attributed via `tasks.shipped_by`**, and the promotion is rank-guarded (only a current `xenos`), so it is idempotent — it fires exactly once, and a non-Xenos never re-promotes.
32
+
33
+ > **Trigger note (corrected in [#704](https://example.com/builders#/task/704)):** the original [#692](https://example.com/builders#/task/692) cut graduated at `confirmed` (credits-earned). The Archon rule is "after 3 tasks are **successfully shipped**," so the trigger moved from the confirm paths to `shipTask`, keyed on `shipped` status + `shipped_by` attribution.
34
+
35
+ **`thetes` is reachable two ways.** The automatic 3-shipped-tasks graduation above, AND a deliberate Archon promotion via `PATCH /builders/:id/rank` (e.g. to fast-track a builder the Archon already trusts to roam the queue). Both are valid; they coexist. (A short-lived [#704](https://example.com/builders#/task/704) cut wrongly refused manual set-to-thetes; [#706](https://example.com/builders#/task/706) removed that block — the Archon must be able to grant the rank directly.)
36
+
37
+ Auto-promotion of *rank* normally raises a trust-boundary flag (ADR 0016: rank is authority). It is acceptable here precisely because **graduating to Thetes grants no authority** — only breadth of claiming. The dangerous grant (Thetes → Metic) remains a deliberate Archon promotion through the single rank chokepoint (`PATCH /builders/:id/rank`). So the machine can hand out "you may now pick up normal work" but never "you may now shape scope or touch sensitive infra."
38
+
39
+ The promotion reuses the existing rank-change machinery (refactored into a shared in-transaction core `_applyRankChangeTx`): it writes the same `credit_log` `rank_change` audit row (here tagged `auto-graduation: N shipped tasks`), earns the standard +5 promotion ceremony delta, does **not** revoke sessions (promotions never do), and fires the post-commit box-access reconcile.
40
+
41
+ ## Consequences
42
+
43
+ - **The newcomer-supply problem shrinks to a 3-task window.** The `newcomer-generator` now only has to keep the queue stocked for a builder's first three ships, not their whole tenure. Starvation after that is structurally impossible — Thetes draws from the general queue.
44
+ - **`metic` is now also marked LIVE** in the onboarding rank-ladder diagram. The diagram fact-extractor (`diagram-facts.wiredRanks`) gained a 5th signal — membership in `REQUIRES_RANK_TIER` — which is the honest definition of "this rank participates in the live claim gate." Previously `metic` was wired-in-spirit (skills said "Metic+") but the extractor missed it.
45
+ - **Exact-set `requireRank` is load-bearing.** Adding a rank *below* an existing gated rank is safe only because the gates name their allowed ranks explicitly. A tier-based `requireRank` would have silently granted Thetes everything ≥ its tier. This is now documented as the reason the exact-set design stays.
46
+ - **`RANKS` array order is load-bearing too.** `setBuilderRank` derives promotion-vs-demotion from `RANKS.indexOf`; `thetes` had to move to sit between `metic` and `xenos` so `xenos→thetes` reads as a promotion and `thetes→metic` as a further promotion.
47
+ - **Reversibility.** A mis-graduated builder is demoted through the normal Archon path; demotion revokes their sessions, so the lower rank takes effect on their next request.
48
+
49
+ ## Alternatives considered
50
+
51
+ - **Just raise the newcomer-queue target / fix supply only.** Treats the symptom. A shared pool of N newcomer tasks is always racing consumption; time-boxing the sandbox removes the dependency entirely for everyone past their 3rd ship.
52
+ - **Auto-promote `xenos` straight to `metic`.** Rejected: Metic carries triage/trust/red-team/infra powers no automatic rule should hand out. The new intermediate rank is exactly the "breadth without trust" tier that makes auto-promotion safe.
53
+ - **Promote by drachmae or karma instead of ship count.** Rejected for v1: ship count is the most legible "you've proven you can land work" signal and is already the credit-landing event. A richer policy can layer on later.
@@ -0,0 +1,128 @@
1
+ # ADR 0035 — Builder onboarding: three setup paths (Local / DO Box / Remote Control)
2
+
3
+ **Date:** 2026-06-06
4
+ **Context:** GDS-V3, task [#715](https://example.com/builders#/task/715). Feeds done-when criterion **C1** (`a Metic onboards in ≤ 2h`). This ADR is the *onboarding-as-experience* companion to [ADR 0031](<redacted>.md), which locked the **substrate** (the containerized DO box, auto-suspend, rank-gated source access) but never consolidated how a person actually *arrives*. Builds on [ADR 0034](<redacted>.md) (the `xenos < thetes < metic < archon` ladder), [ADR 0016](<redacted>.md) (server-enforced permissions), [ADR 0022](0022-secrets-policy.md) (no secrets in repo), and the box onboarding middleware shipped in [#701](https://example.com/builders#/task/701) (§9.4 of ADR 0031).
5
+ **Status:** Accepted — §2 (the first-box Archon approval gate) superseded by [ADR 0059](<redacted>.md). *Decision record — locks the onboarding shape and three new policy decisions. The box-lane machinery already exists (ADR 0031 + [#701](https://example.com/builders#/task/701)); the deltas this ADR introduces (the Metic+ local gate, the first-box approval gate, the single hosted guide surface, the primer rewrite) are implementation sub-tasks listed at the end and are NOT yet built.*
6
+ **Track:** `internal` (dev-system / builder onboarding).
7
+
8
+ > **⚠️ Superseded in part by [ADR 0059](<redacted>.md) (task [#1141](https://example.com/builders#/task/1141)).** Decision **§2 — "A newcomer's FIRST box requires Archon approval"** is **removed**: with admission now invite-gated ([ADR 0050](<redacted>.md)), member-join is the sole human gate, and an admitted builder's first box now provisions hands-off. §1 (Metic+ local clone) and §3 (the single hosted guide) stand unchanged. Read §2 below as historical.
9
+
10
+ ## Problem
11
+
12
+ A new builder can reach Example three different ways, but the project has never written down what the **arrival experience** is for each, who each is for, or how they're gated. ADR 0031 decided *where the worker runs* (a box we control); it did not decide *how a stranger goes from "I heard about this" to "I'm shipping tasks."* Three concrete gaps force this ADR:
13
+
14
+ 1. **The live onboarding contradicts the intended model.** Today the *only* fully-working setup path is the one documented in [`docs/onboarding/primer.md`](../onboarding/primer.md) "Get set up": **accept a GitHub collaborator invite and clone the private repo onto your own disk** — open to *every* rank, including a brand-new Xenos. ADR 0031 §6 says source access should be a *rank-gated, revocable, box-held* boundary. So the live behavior is the exact opposite of the locked design: an unproven newcomer currently gets a permanent local copy of the whole repo. This must be reconciled.
15
+ 2. **The box lane is built but has no front door.** ADR 0031 + [#701](https://example.com/builders#/task/701) shipped the whole provisioning spine — `POST /box/ensure`, the intent queue, the control-plane runner, the pairing-link path. But there is no *guided arrival*: nothing tells a non-technical or Chromebook builder, in plain language, "here is your first session, here is what to type, here is how you wait for your box." The capability exists; the on-ramp doesn't.
16
+ 3. **Cost exposure is unbounded at the front door.** Anyone with a GitHub account can complete Device-Flow auth and become a Xenos, and a Xenos is above the box provisioning floor (`DEFAULT_PROVISION_FLOOR = 'xenos'`, [`src/bongos/boxes.js`](../../src/bongos/boxes.js)). With [#701](https://example.com/builders#/task/701)'s auto-draining intent queue, a signup could cause a paid droplet (~$24/mo, ~$5/active after auto-park) to be created with **zero human in the loop**. On a $5,000/yr budget that's a real hole.
17
+
18
+ ## The three paths (what each *is*, who it's for, how it's gated)
19
+
20
+ Onboarding sorts every builder into exactly one of three on-ramps. All three share one foundation — the GitHub Device-Flow auth that `/builder-setup` already performs (`scripts/gds/setup.js`) and that writes the GDS session token — and all three end at the same place: a builder who can `/builder-start`, claim, and ship. They differ only in **where the worker runs** and **how the builder reaches it**, exactly as ADR 0031's three-pieces model (screen / worker / brain) predicts.
21
+
22
+ | Path | Worker runs | Reached via | Who it's for | Rank gate |
23
+ |---|---|---|---|---|
24
+ | **1. Local** | The builder's own machine | Native Claude Code / Desktop **Local** | Trusted power users who want to avoid box cost | **Metic+ only** |
25
+ | **2. DO Box** | A per-builder DigitalOcean box | Claude Desktop → **SSH** | The default for completely new users (Mac/Windows) | Xenos+ (first box needs approval) |
26
+ | **3. Remote Control** | The *same* per-builder DO box | Browser → **Remote Control** (`claude rc`) | Chromebook / iPad / thin-device users | Xenos+ (first box needs approval) |
27
+
28
+ ### Path 1 — Local setup (Metic+ only)
29
+
30
+ - **User group.** Trusted power users who would rather run the repo on their own hardware than pay for (or wait on) a box.
31
+ - **What it is.** Claude Code pointed at a local folder that is a clone of the private GitHub repo — today's flow, unchanged in *mechanics*.
32
+ - **The gate (new policy).** **Local is Metic+ only.** A new user must **not** be able to clone the repo onto their own disk; for everyone below Metic, all source lives inside the box (Path 2/3). This aligns the live behavior with ADR 0031 §6/§9.2, where full source creds first arrive at graduation to Metic. Below Metic, the local on-ramp is simply not offered, and the collaborator-invite path is closed to them.
33
+ - **Onboarding steps.** Same as the current flow: install Claude Code, accept the repo invite, clone into an empty folder, `/builder-setup`, read the primer → `/builder-primer-read` → `/builder-start`.
34
+
35
+ ### Path 2 — DO Box setup (the newcomer default)
36
+
37
+ - **User group.** Completely new users on a Mac or Windows machine.
38
+ - **What it is.** Claude Code runs *on* an org-controlled, containerized DigitalOcean box provisioned for that builder; the builder's Claude Desktop connects to it over SSH. The repo never lands on the builder's personal disk.
39
+ - **The gate.** Above the `xenos` provisioning floor, so any signed-in builder qualifies — **but the first droplet for a builder who has never had a box requires Archon approval before it is created** (new policy; see Decision §2). "One droplet at a time" is **already enforced**: `box_intents` carries a partial unique index (one open intent per builder) and `builder_boxes` is one row per builder.
40
+ - **Onboarding steps.**
41
+ 1. The new user opens a simple local Claude session pointed at the **hosted onboarding guide** (Decision §3) — the same content that drives the local-onboarding walkthrough today, but aimed at provisioning a box rather than cloning a repo.
42
+ 2. Claude walks the user through requesting a box (`/builder-box` → `POST /box/ensure`), which records a provision intent. The user is told their first box needs a quick approval, and waits.
43
+ 3. Once approved + provisioned, the user opens a new session, their Claude Desktop SSHes into the box, and they are now a builder in Example. The source surface they see is rank-scoped (`starter` sparse-checkout for Xenos/Thetes; `full` at Metic+ — ADR 0031 §9.2).
44
+
45
+ ### Path 3 — Remote Control (Chromebook / thin devices)
46
+
47
+ > **⚠️ Superseded by [ADR 0038](<redacted>.md).** The `claude rc` headless-service mechanism described below was retired — a headless box cannot bootstrap `claude rc` (interactive-only Trusted-Devices enrollment). The box now serves its own `ttyd` browser terminal over a Cloudflare Tunnel where the builder runs normal interactive `claude`. The Path-3 *intent* (a browser-only thin-device on-ramp to the same per-builder box) stands; the `claude rc`-as-always-on-service mechanics below are historical.
48
+
49
+ - **User group.** Users on a device that cannot run Claude Desktop (a literal Chromebook, an iPad, a phone).
50
+ - **What it is.** Identical to Path 2 — the same per-builder box — but reached from a browser via Claude's Remote-Control (`claude rc` running as an always-on service on the box).
51
+ - **Onboarding steps.**
52
+ 1. Same as Path 2 steps 1–2, except the guide surface is the **hosted onboarding page in a browser tab** rather than a local Claude folder (a Chromebook has no local dev environment to point Claude at). It is the *same content* (Decision §3) — one guide, two ways to read it.
53
+ 2. After the box is provisioned, the user pairs: `POST /box/ensure` → poll `GET /box/pairing-link` until the `claude rc` link appears (published UP by the box's `box-report-pairing` cron, [#701](https://example.com/builders#/task/701)) → open the link. Claude then drives the box from the browser.
54
+
55
+ ## Decision
56
+
57
+ The three paths above are the canonical onboarding model. Beyond consolidating them, this ADR locks **three policy decisions** (workshopped with Lars, 2026-06-06) and a **rollout sequence**.
58
+
59
+ ### 1. Local source access is Metic+; everyone below is container-only
60
+
61
+ The <redacted> + clone-to-own-disk path becomes a **Metic+ affordance**. Below Metic (Xenos, Thetes), there is **no local clone** — source exists only inside the rank-gated box, where it is revocable and audited (ADR 0031 §9.2). This is the onboarding-side enforcement of the trust boundary: *proven* builders may hold a local copy; *unproven* ones work inside the box. Mechanically this means (a) the primer stops instructing newcomers to clone, and (b) collaborator invites are issued only at/after Metic.
62
+
63
+ > **Honest limit (carried from ADR 0031 §6/§9.2).** A Metic with a local clone, or any authorized builder with a box session, can still copy code they can read. This is defense-in-depth + revocation + audit, not insider-proof DRM. The Metic+ gate raises the bar from "any signup gets the repo forever" to "only a proven, promotable builder does."
64
+
65
+ ### 2. A newcomer's FIRST box requires Archon approval
66
+
67
+ [#701](https://example.com/builders#/task/701) made `POST /box/ensure` enqueue a provision intent that the control-plane runner auto-drains. That is correct for *wakes* and for *re-provisions* of a builder who has earned a box before — but the **first ever provision for a builder** is now gated:
68
+
69
+ - A `provision` intent for a builder with **no prior box** (no `builder_boxes` row, or one that has never been `active`) is held in a **pending-approval** state instead of being drained. The runner skips it until an Archon approves.
70
+ - An Archon approves via an Archon-only affordance (a route + a `box.js` operator command — exact surface is an implementation detail; the decision is *that approval gates the first provision*). Approval flips the intent to drainable; the existing runner then provisions it unchanged.
71
+ - **`wake` intents and any subsequent provision** (a builder who has had an `active` box before) are **not** gated — they auto-drain as today. The gate is specifically the *first* paid droplet for an unproven account.
72
+
73
+ This closes the §Problem-3 cost hole: a stranger can sign in and read, but cannot cause us to spend money until a human says so. It rides the existing `box_intents` queue (add an approval state/column) rather than introducing a new mechanism, and it keeps the §6 trust-boundary invariant intact — the web tier still only *asks*; it never holds `DO_API_TOKEN` and never provisions directly.
74
+
75
+ > **Why not an invite-code gate (the considered alternative).** Closing signup entirely (invite codes) is the strongest cost+security control, but it adds a distribution burden and forecloses the open, low-friction "anyone can come look" funnel C1 implies. First-box approval keeps the funnel open (sign up, read, file ideas) while still putting a human between a signup and a charge. Revisit invite codes if signup-driven box spend ever becomes a real problem.
76
+
77
+ ### 3. One hosted guide surface for both SSH and RC — no separate bootstrap repo
78
+
79
+ The pre-box "guide Claude" needs onboarding context, never source. Rather than build and maintain a **separate private bootstrap repo** (the original sketch), there is **one hosted onboarding guide** served from the GDS:
80
+
81
+ - **SSH/Desktop users** point a local Claude session at it (a hosted page, or the existing public `GET /box/connect` / `connect.sh` bootstrap it links to).
82
+ - **RC/Chromebook users** open the same content as a **browser page** — they have no local folder to point Claude at.
83
+
84
+ Same content, two ways to consume it. This avoids a second repo to keep in sync, and the *executable* half already exists (the public, `PUBLIC_ORIGIN`-pinned `connect.sh`/`.ps1` from [#685](https://example.com/builders#/task/685)). The new artifact is the **human/Claude-readable guided walkthrough** that frames the whole arrival — sign in → request box → wait for approval → connect/pair — and that is safe to serve publicly because it contains zero source.
85
+
86
+ ### 4. Rollout — gate local to Metic+ now, keep it as a fallback until the box lane is hardware-verified
87
+
88
+ The box lane (Paths 2 & 3) is **built but unverified on real hardware**, and the box-side source credential is **inert** (`configured:false` — the prod secret is unset; ADR 0031 §9.2/§9.4, [#699](https://example.com/builders#/task/699)). We must never leave **zero** working paths. Therefore:
89
+
90
+ - **Now:** apply Decision §1's Metic+ gate to the local path, and make Path 2 the *documented* default for newcomers.
91
+ - **Keep:** the collaborator-invite local path as a **Metic+-only fallback** — it stays the proven escape hatch until the box lane works end-to-end on hardware.
92
+ - **Then:** once the verification plan below is green, the box lane becomes the real newcomer default and the local path settles into its permanent Metic+ niche.
93
+
94
+ ## Verification plan (what "test the existing setup" means after this ADR)
95
+
96
+ ADR 0031 §9 + [#699](https://example.com/builders#/task/699) + [#701](https://example.com/builders#/task/701) record the box lane as hermetically tested but **hardware-unverified**. This ADR's acceptance does not assert the box lane *works* — it asserts the *shape*. The following legs must be exercised on a real box before Path 2/3 can be the default (this is the test campaign that follows this ADR):
97
+
98
+ 1. **Source credential goes live.** Set the prod `BOX_SOURCE_*` secret(s) so `GET /box/source-access` returns `configured:true`; verify a real box sparse-clones the `starter` surface (Xenos) and the `full` surface (Metic) and that the token is scrubbed from `.git/config` (`infra/box-source-fetch.sh`). *Operator gotcha (recorded):* `DO_API_TOKEN` must carry **tag-create** scope or provisioning 422s.
99
+ 2. **Auto-provision via the timer.** Install `box-intent-runner.timer` on the control plane; confirm a `POST /box/ensure` intent is drained into a live droplet — **including the new first-box approval gate**: an unapproved first intent stays pending; an Archon approval lets it drain.
100
+ 3. **Wake-on-connect.** Park a box, confirm `POST /box/ensure` wakes it from snapshot.
101
+ 4. **Desktop SSH connect.** A real Claude Desktop connects to a running box via the managed-settings host (the [#599](https://example.com/builders#/task/599) stable-hostname cutover) and runs Claude Code on it.
102
+ 5. **RC pairing end-to-end.** `box-report-pairing` publishes a real `claude rc` link; a browser reads it via `GET /box/pairing-link` and drives the box.
103
+ 6. **Guide + primer dry-run.** A fresh GitHub account, following only the hosted guide + rewritten primer, reaches "claimed and shipped a task" within the C1 ≤2h budget — the actual onboarding-time measurement.
104
+
105
+ ## Consequences
106
+
107
+ - **The live primer must be rewritten.** [`docs/onboarding/primer.md`](../onboarding/primer.md) "Get set up" currently teaches only the open-to-all local clone. It must present the three paths, default newcomers to the box, and apply the Metic+ local gate. The ADR 0031 three-pieces mental model (screen/worker/brain) lands here verbatim, as 0031 §"mental model" always intended.
108
+ - **Collaborator-invite issuance becomes rank-aware.** Invites are issued at/after Metic, not on signup. (The mechanics of *who* sends invites and *when* is an operator step today; the policy is now written.)
109
+ - **`box_intents` gains an approval state.** A small schema/route/CLI addition; the runner learns to skip unapproved first-provisions. No new subsystem.
110
+ - **One thing to maintain, not two.** The single hosted guide means no second repo drifting out of sync — but it does mean the guide content is a real artifact someone owns and keeps current.
111
+ - **Cost stays bounded at the door.** Open, free signup + read; money only after a human approves the first box. Aligns with the §5 cost model of ADR 0031.
112
+ - **No relitigating 0031.** Substrate, auto-suspend, source-scoping, and the intent queue are unchanged. This ADR only adds the onboarding-experience layer and the three policy gates on top.
113
+
114
+ ## Implementation sub-tasks (proposed — to be seeded in a planning/triage session)
115
+
116
+ Not yet created; listed so a build session has a starting decomposition. None are built by this ADR.
117
+
118
+ 1. **Metic+ local gate.** Stop the primer/onboarding from offering local clone below Metic; make collaborator-invite issuance Metic+.
119
+ 2. **First-box approval gate.** Add a pending-approval state to `box_intents` (schema + the runner skip), an Archon-only approve affordance (route + `box.js` command), and surface "your box is awaiting approval" to the waiting builder via `/builder-box`.
120
+ 3. **Hosted onboarding guide.** Author the single public guided walkthrough (page + the content a local guide-Claude reads), framing sign-in → request box → await approval → connect/pair, for both SSH and RC. Reuses the existing public `connect.sh`/`.ps1`.
121
+ 4. **Primer rewrite.** Replace the local-only "Get set up" with the three-path model + the 0031 mental model; keep `/builder-primer-read` as the gate.
122
+ 5. **Run the verification plan.** The six-leg hardware campaign above — this is the "test the existing setup" work that motivated the ADR.
123
+
124
+ ## Open questions (flagged, not blocking)
125
+
126
+ 1. **Approval surface ergonomics.** Should first-box approval be a one-click action in the builders' hall, a Discord interaction (ADR 0033), or an operator CLI only? Lean: builders'-hall action + `box.js` fallback; decide at build time.
127
+ 2. **Approval expiry.** Does an approved-but-undrained intent expire, and does a denied newcomer get told why? Lean: approvals don't expire; denials leave an audited note.
128
+ 3. **Metic transition off the box.** When a Xenos/Thetes on a box graduates to Metic and *wants* the local path, what's the handoff (re-clone locally, retire the box, or keep both)? Touches ADR 0031 open-question [#1](https://example.com/builders#/task/1) (ownership transition); defer.
@@ -0,0 +1,40 @@
1
+ # ADR 0036 — Builders subdomain + builder-UI reorganization (hall / watch / settings / status)
2
+
3
+ **Date:** 2026-06-06
4
+ **Context:** GDS-V3, task [#726](https://example.com/builders#/task/726). Builds on [ADR 0016](<redacted>.md) (server-enforced permissions), [ADR 0029](<redacted>.md) / [ADR 0030](<redacted>.md) (the status + hall surfaces being reorganized), and the asset-stamping serving model in `server.js` / `src/asset-version.js`.
5
+ **Status:** Accepted. Shipped under [#726](https://example.com/builders#/task/726) (grader skipped at Lars's instruction — UI/infra reorg, no logic the grader gates).
6
+ **Track:** `internal` (dev-system / builder UI).
7
+
8
+ ## Problem
9
+
10
+ The builder-facing web had grown into one overloaded surface. The authenticated hall (`example.com/builders`) carried *everything*: a builder's personal standing, the per-skill model picker, AND three Archon-only monitoring panels (security watch, the access-request gate, the roster). Meanwhile the public status dashboard (`status.example.com`) carried operational engineering detail (the Stonemason's Mark grade aggregate, the Surveyor's estimation drift, the Mason's Ledger repo health) that is really *builder* monitoring, not something a public visitor needs. There was no single, easily-gated home for "the builder surface," and no dedicated place for an Archon to watch the GDS and the other builders.
11
+
12
+ ## Decision
13
+
14
+ Three surfaces, cleanly separated by audience:
15
+
16
+ 1. **`builders.example.com` — a dedicated subdomain for the entire builder surface.** It serves the same `public-builders/` tree, rooted at `/`, so the whole authenticated builder UI sits behind one host that is easy to reason about and gate. Implemented as a thin **rewrite shim** in `server.js` (`isBuildersHost`): every non-API path on the builders host is rewritten onto the existing `/builders/*` tree, so all the established serving + sign-in gating + asset-stamping logic is reused verbatim — zero duplication. The legacy `example.com/builders/*` paths keep working unchanged, so the subdomain is **purely additive**: nothing breaks before DNS + cert land, and existing links / primer nudges / the OAuth callback (`→ /builders`) are untouched.
17
+
18
+ 2. **`builders.example.com/watch` — a new Archon-only monitoring page** ("The Watch"). Sections, in order: **GATE** (pending access requests), **WATCH** (security: open vuln reports + override requests + the audit log), **ROSTER** (the citizen-roll with rank/status actions), **MARKS** (per-builder grade breakdown — new endpoint), **STONEMASON'S MARK** (project-wide grade aggregate, moved off status), **SURVEYOR** (estimation drift, moved off status), **MASON'S LEDGER** (repo health, moved off status), **BFG** (session-ledger tally + link). Gated server-side by `requireArchonPage` on the shared `/builders/watch` path (one matcher covers both hosts since the subdomain rewrites `/watch` → `/builders/watch`); every data endpoint it reads is independently `requireRank('archon')`, so the page gate is defense-in-depth, not the only wall.
19
+
20
+ 3. **`builders.example.com/settings` — a new personal-preferences page.** Holds **VOICES** (the per-skill model picker, moved off the hall) and **CRAFT** (discipline preferences, moved off the now-read-only Standing card). Open to any authenticated builder.
21
+
22
+ The **hall** (`builders.example.com`) is now a citizen's *personal* dashboard, reordered to: STANDING, HONOURS, KARMA (with the peer-acclaim feed folded under it), WORKS (ships wrought), CLAIM, then MARKS, BOUNTY, ROLL — onboarding WELCOME/PATH still lead for newcomers. The Archon panels and the model picker are gone from it (moved to /watch and /settings respectively); their nav links live in the header gated-nav (the Watch link reveals only for Archons).
23
+
24
+ The **status dashboard** (`status.example.com`) is slimmed to what a public visitor cares about, reordered to: PULSE, WATCH (uptime), ARCHITECTS, WORKS, TREASURY, LATELY INSCRIBED, THE WORLD. The three engineering tablets moved to /watch.
25
+
26
+ ### Cross-subdomain auth
27
+
28
+ A single sign-in must span `example.com`, `builders.`, and `status.`. The `gds_session` cookie is now scoped to **`.example.com`** on the production-facing hosts (enumerated allowlist: apex, www, builders, status) and stays **host-only** on localhost and `staging.example.com` — staging is a separate process + DB, so an apex cookie there would bleed a token across environments. The OAuth handshake still runs on the canonical origin (the builders subdomain links sign-in at the apex), so GitHub's fixed redirect_uri registration is unaffected. A validated `?return=` parameter (open-redirect guarded to relative paths + `*.example.com` https URLs only) lets the subdomain bounce a stranger through the apex handshake and land them back on the subdomain. Logout clears both the host-only and apex-scoped cookie forms.
29
+
30
+ ## Alternatives considered
31
+
32
+ - **Duplicate the serving logic for the subdomain.** Rejected — the rewrite shim reuses the whole `/builders/*` pipeline; a second copy would drift.
33
+ - **Hard-redirect `/builders/*` → the subdomain.** Rejected (for now) — the subdomain needs DNS + cert first; redirecting before that would dark the hall. The subdomain is additive; a redirect can come later once it's verified live.
34
+ - **`/watch` WATCH = uptime (mirror status).** Considered (the status spec called WATCH a status-mirror), but Lars chose `/watch` WATCH = the *security* watch — the Archon's real monitoring concern. Status keeps its own uptime WATCH for the public.
35
+
36
+ ## Consequences
37
+
38
+ - **Operator one-time setup** (until then everything stays reachable at `example.com/builders/*`): add a Cloudflare DNS A record `builders` → `REDACTED_IP` (proxy ON); confirm the origin cert's `*.example.com` SAN covers it; `caddy reload`. The `builders.example.com` Caddy block ships in `infra/Caddyfile`.
39
+ - New endpoint `GET /api/gds/grades/by-builder?days=` (Archon-only) backs the MARKS section.
40
+ - Dead render functions for the moved hall sections remain defined-but-unreferenced in `builders.js` (all guarded); a follow-up cleanup can delete them.
@@ -0,0 +1,51 @@
1
+ # ADR 0037 — Bot-managed Discord channels from a config file, Archon-gated commands
2
+
3
+ **Status:** Accepted — 2026-06-06
4
+ **Context task:** [#727](https://example.com/builders#/task/727) (GDS-V3). Absorbs idea_inbox [#200](https://example.com/builders#/idea/200) ("discord builders channel role access auto managed by the example bot").
5
+ **Companions:** [ADR 0032](0032-discord-mirror-of-gds.md) (Discord = one-way window into the GDS), [ADR 0033](<redacted>.md) (the bot is a service principal, never an authority), [ADR 0016](<redacted>.md) (the trust boundary), [ADR 0026 §6C](<redacted>.md) (the <redacted> write pattern this reuses).
6
+
7
+ ## Context
8
+
9
+ The Example Discord has a fixed channel layout (PUBLIC: welcome, ship-news, agora, support; BUILDERS: ship-feed, ideas, red-team, planning, archon-only). Until now those channels were created and described by hand. Lars asked for two things:
10
+
11
+ 1. The bot should **create and edit channels itself** (topics, and which ranks may see each channel — the access question from idea [#200](https://example.com/builders#/idea/200)).
12
+ 2. **Lower ranks cannot command the bot.** Any *command* that makes the bot act must be **Archon-only**; the bot's **passive** features (ship-feed applause counting, #ideas inbound filing, rank role badges) stay open to everyone, as designed in ADR 0033.
13
+
14
+ The risk this design must avoid is the one ADR 0016 closes: a Discord action becoming an authority. Channel layout is cosmetic, but "the bot edits the server on command" is still a command surface that must be gated where it is load-bearing — the server, against the live `builders.rank`.
15
+
16
+ ## Decision
17
+
18
+ **1. The channel layout is a repo config file, not bot state.** `docs/discord/channels.json` is the single source of truth: each category/channel declares its `name`, `topic`, and `view` (who may see it — `everyone` or a rank name, where a rank grants that rank and every rank above it). Because changing channels means editing a repo file and merging it, and merging is itself rank-gated, the layout is **Archon-gated by construction**.
19
+
20
+ **2. The bot reconciles one-way (config → Discord), best-effort, never destructive.** `src/bongos/discord-channels.js` matches existing channels by **slug** (emoji + separators stripped, lowercased — so `📯-ship-news` matches `ship-news`), edits topics + view-permissions **in place**, and creates anything missing. It **never deletes** a channel. This mirrors role-sync (ADR 0033 §4): read the repo, write Discord, no path back. View-permissions are applied as Discord permission overwrites for the `VIEW_CHANNEL` bit only — every other permission bit on a channel, and every unmanaged role's overwrite, is preserved.
21
+
22
+ **3. The command surface is Archon-only, server-enforced.** `src/bongos/routes/discord.js` exposes `GET /api/gds/discord/channels/plan` (dry run) and `POST /api/gds/discord/channels/reconcile` (apply), both behind `requireBuilder` + `requireRank('archon')`. A Metic, Thetes, Xenos, or unauthenticated caller gets a 403. The CLI (`scripts/gds/discord-channels.js plan|apply`) is a thin wrapper over these routes, so the gate stays on the server — a builder editing local files cannot bypass it (ADR 0016).
23
+
24
+ **4. The passive sweep is kill-switched, default OFF.** The bot also reconciles channels on connect + every 6h, but only when `DISCORD_CHANNELS_MANAGED=true` (mirrors `BFG_WRITE_ENABLED`, fail-closed). With the switch off, channels are touched **only** by an explicit Archon command. This stops the bot from surprise-editing a guild the moment it connects.
25
+
26
+ **5. Passive engagement features are untouched.** Applause, #ideas inbound, and role badges remain open to all ranks. "Lower ranks can't use the bot" means **they can't command it**, not that they can't be served by it.
27
+
28
+ ## Access mapping (defaults — editable in the config)
29
+
30
+ | Channel | view | Who sees it |
31
+ |---|---|---|
32
+ | welcome, ship-news, agora, support | `everyone` | all (players included) |
33
+ | ship-feed | `everyone` | all — keeps the ADR 0033 §3 "anyone can applaud" rule true |
34
+ | ideas | `everyone` | all — keeps inbound open; attribution still requires a link |
35
+ | red-team | `metic` | Metic + Archon (matches `/builder-redteam`) |
36
+ | planning | `metic` | Metic + Archon (matches the planning/priority skills) |
37
+ | archon-only | `archon` | Archon only |
38
+
39
+ These live in `channels.json` and are reviewed/changed by editing that file.
40
+
41
+ ## Consequences
42
+
43
+ - **Trust boundary holds.** Channels are a cosmetic reflection of an Archon-reviewed file; the command that applies them re-checks live rank server-side. The bot never decides anything.
44
+ - **Go-live prerequisite (human, browser).** The bot must hold Discord's **Manage Channels** permission and sit above the managed channels — same shape as the Manage Roles prereq in ADR 0033. Without it, reconcile ops 403 and are logged; the GDS is unaffected. Tracked as a blocker on [#727](https://example.com/builders#/task/727).
45
+ - **Live verification deferred.** The pure planner is fully unit-tested (`tests/discord_channels.mjs`); real channel edits require the bot on + Manage Channels and are verified at go-live.
46
+
47
+ ## Alternatives considered
48
+
49
+ - **Discord slash command (`/channel create`).** Rejected for now — a new in-Discord command surface to secure, when the repo config already gives an Archon-gated, reviewable, version-controlled source of truth.
50
+ - **Pure Discord role permissions, no bot.** Rejected — it would make Discord roles the authority for "who can change channels," exactly the inversion ADR 0016 forbids; and it can't keep topics/structure honest.
51
+ - **Bot writes with its own authority / no kill-switch.** Rejected — re-opens ADR 0016 and risks a connect-time surprise edit. The command gate + default-OFF passive sweep is the whole point.
@@ -0,0 +1,111 @@
1
+ # ADR 0038 — Chromebook / browser on-ramp via a box-hosted ttyd web terminal over a Cloudflare Tunnel
2
+
3
+ **Date:** 2026-06-06
4
+ **Context:** GDS-V3, task [#760](https://example.com/builders#/task/760). Feeds done-when criterion **C1** (`a Metic onboards in ≤ 2h`) by restoring a *working* thin-device on-ramp. Companion to [ADR 0031](<redacted>.md) (the per-builder DigitalOcean box substrate), [ADR 0035](<redacted>.md) (the three onboarding paths), [ADR 0016](<redacted>.md) (server-enforced trust boundary — the web tier only *asks*, never holds infra power), and [ADR 0022](0022-secrets-policy.md) (no secrets in repo).
5
+ **Status:** Accepted. **Supersedes the Remote-Control (path 3) approach** in [ADR 0035](<redacted>.md) Path 3 and [ADR 0031](<redacted>.md) §1 / §9.4 (the `claude rc` thin-device on-ramp). Implemented this session under [#760](https://example.com/builders#/task/760).
6
+
7
+ > **Update (2026-06-07, [ADR 0040](<redacted>.md)):** the "Remote Control is unfixable" conclusion below was specific to running RC *headless, as a background service*. Once this ADR's ttyd web terminal put a real human at a real PTY, Remote Control works — started in-session with `/remote-control` (not the standalone `claude rc`). RC is now the **default** browser-only experience (shipped in [#807](https://example.com/builders#/task/807)); the ttyd terminal is its one-time launchpad. The blockers in *Problem* still hold for *headless* RC. See ADR 0040.
8
+ **Track:** `internal` (dev-system / builder onboarding).
9
+
10
+ ## Problem
11
+
12
+ A per-builder cloud dev box ([ADR 0031](<redacted>.md)) is reached two ways: Mac/Windows builders via Claude Desktop + SSH (works), and Chromebook / browser-only builders via — originally — Claude's **Remote Control** (`claude rc`) running as a headless systemd service (`infra/claude-rc.service`), with the box publishing a claude.ai pairing link UP to the GDS so the builder reaches it from a browser ([ADR 0031](<redacted>.md) §9.4, [#701](https://example.com/builders#/task/701)).
13
+
14
+ Task [#745](https://example.com/builders#/task/745) leg-5 verification-tested that path on hardware and found it **broken end to end**. This session's research (June 2026, Claude Code CLI v2.1.x) confirmed it is **not** fixable by plumbing — there are three hard blockers, all on Anthropic's side and by design:
15
+
16
+ 1. **Trusted Devices is interactive-only.** "Trusted Devices for Remote Control" is an org/account policy that requires interactive `/login` device enrollment in a TTY. There is no headless / baked-credential / env-var enrollment path. Observed live: `claude rc` errors `Your organization requires Trusted Devices for Remote Control, but this device is not enrolled. Please run /login in Claude Code to enroll this device.`
17
+ 2. **`claude rc` needs a controlling TTY.** It requires a controlling TTY and only renders the pairing URL to a TTY. A systemd service has none, so the URL is never captured.
18
+ 3. **Remote Control needs full-scope subscription OAuth.** Every non-interactive auth path (`ANTHROPIC_API_KEY`, `claude setup-token` / `CLAUDE_CODE_OAUTH_TOKEN`, `apiKeyHelper`) is inference-only and is **explicitly excluded** from Remote Control.
19
+
20
+ Net: a headless box can never bootstrap `claude rc`, and a no-SSH Chromebook builder cannot perform the interactive enrollment *on* the box either. Dead end.
21
+
22
+ ## Decision
23
+
24
+ **Drop `claude rc` entirely. The box serves its OWN browser session: a `ttyd` web terminal, exposed through an outbound-only Cloudflare Tunnel, dropping the builder into a real interactive shell where they run NORMAL `claude`.**
25
+
26
+ ### 1. ttyd — a real browser-backed PTY on the box
27
+
28
+ `ttyd` (a lightweight web-terminal daemon) binds to `127.0.0.1:7681`, basic-auth protected with a per-box generated password, and drops the builder into an interactive login shell in `/workspace`. The builder runs ordinary interactive `claude` inside it.
29
+
30
+ This dissolves all three blockers because there is now a **real human at a real browser-backed PTY**. So `/login` (the OAuth flow opens in the builder's own browser tab), workspace-trust acceptance, **and** Trusted-Devices enrollment all just work — the exact interactive steps that are impossible headlessly.
31
+
32
+ ### 2. Exposure — an outbound-only Cloudflare Tunnel, $0 added cost
33
+
34
+ A `cloudflared` Cloudflare Tunnel routes the builder's browser to the local ttyd. The box opens **no inbound port** — preserving the firewall posture that made `claude rc`'s outbound websocket attractive in the first place, but this one actually works. Two modes:
35
+
36
+ - **(a) Quick tunnel (default).** A tokenless tunnel at a random `https://<id>.trycloudflare.com` that needs **zero operator secrets**.
37
+ - **(b) Named tunnel (production upgrade).** A stable, branded hostname on `*.dev.example.com` when the operator configures a connector token — the documented production upgrade.
38
+
39
+ Cloudflare Tunnel is free (Zero Trust free tier, 50 users) — **$0 added cost**.
40
+
41
+ ### 3. Delivery — reuse the existing trust-boundary-safe plumbing
42
+
43
+ The box reports its current terminal URL + credential UP to the GDS (`POST /api/gds/box/terminal`, authenticated as the builder via the on-box GDS token); the builder reads them from the browser (`GET /api/gds/box/terminal`, own-resource only, `requireBuilder`). This is the same shape as the migration 070 / [#701](https://example.com/builders#/task/701) pairing path, renamed from the old `/box/pairing(-link)`. New DB columns `terminal_url` / `terminal_credential` / `terminal_at` (migration 073); the old `remote_pairing_*` columns are **deprecated in place**.
44
+
45
+ ### 4. Security model
46
+
47
+ - **Localhost binding.** ttyd binds to `127.0.0.1` — only the tunnel reaches it.
48
+ - **Basic auth.** HTTP basic auth with a per-box random password, stored mode-600 at `/etc/otb/terminal-credential` and persisted in the box snapshot across park/wake.
49
+ - **Secret containment.** The credential is returned ONLY to the box's own builder via `GET /box/terminal`; it never appears on any public surface or in `box_events`.
50
+ - **Revocation.** Rides the existing rank/status gate + box reconcile: a demoted/deactivated builder loses source access and the box is deprovisioned ([ADR 0031](<redacted>.md) §9.2).
51
+ - **Defense in depth.** An unguessable tunnel URL **and** basic auth, both over HTTPS.
52
+
53
+ ## Consequences
54
+
55
+ **Good:**
56
+ - Works on any browser, including a literal Chromebook.
57
+ - No new operator secret in the default (quick-tunnel) path.
58
+ - No inbound ports — the box's egress-restricted firewall posture is preserved.
59
+ - Reuses the existing GDS plumbing (intent queue + report-up/read-down), no new subsystem.
60
+ - The builder gets the full real Claude Code TUI; `claude` auth/enrollment is a normal browser click rather than an impossible headless step.
61
+
62
+ **Trade-offs / honest limits:**
63
+ - **Quick-tunnel URLs are random and change on each `cloudflared` restart.** Handled — the box re-reports the URL each boot, the same ephemeral-URL-per-boot shape the old claude-rc reporter used.
64
+ - **`trycloudflare.com` is positioned for testing, not production SLA** — hence the named-tunnel upgrade for scale.
65
+ - **The per-box basic-auth password is long-lived** (until box rebuild) rather than single-use.
66
+ - **ttyd adds a tiny always-on service** on the box.
67
+
68
+ **Files:**
69
+ - Added: `infra/box-terminal.service` (ttyd unit), `infra/cloudflared.service` + `infra/cloudflared-run.sh` (tunnel, both modes), `infra/box-report-terminal.sh` (reporter), cloud-init install + wiring, migration 073, `src/bongos/boxes.js` `setTerminalAccess`, `src/bongos/routes/box.js` `GET/POST /box/terminal`.
70
+ - Deleted: `infra/claude-rc.service`, `infra/get-pairing-link.sh`, `infra/box-report-pairing.sh`.
71
+
72
+ **Follow-up:**
73
+ - **Named-tunnel automation — DONE ([#771](https://example.com/builders#/task/771)).** The control plane creates the per-builder named tunnel + proxied DNS CNAME (`term-<login>.example.com → <id>.cfargotunnel.com`) via the Cloudflare API token and bakes `BOX_TUNNEL_TOKEN` + `BOX_TERMINAL_HOSTNAME` into the box's `/etc/otb/box.env` on `provision`, tearing both down on `deprovision`. OPT-IN (`BOX_NAMED_TUNNEL=1`, default OFF → quick tunnel). Client: `src/bongos/cf-tunnel.js` (mirrors `do-api.js` — injectable fetch, dry-run default, **forces IPv4** since the CF token is IP-locked); orchestration in `scripts/gds/box.js`. See the recipe's "Named-tunnel automation" section.
74
+ - **Hardware-verified live ([#771](https://example.com/builders#/task/771)).** Provisioned a real box: tunnel create + ingress + CNAME + token-bake + teardown all confirmed against the live Cloudflare account; `https://term-example-owner.example.com` served ttyd through the tunnel (401 without creds, 200 with). The walk surfaced three fixes now landed: **(a)** IPv4-pinning in `cf-tunnel.js`; **(b)** the apex-not-`dev.*` hostname default — free Universal SSL covers `*.example.com` (one label) but not a two-label `dev.*` host, which fails TLS at the edge; **(c)** masking the Ubuntu `ttyd` apt package's own auto-started, **credential-less** `ttyd.service` (it grabbed port 7681 → our unit crash-looped *and* an auth-less terminal would have been exposed through the tunnel).
75
+ - **⚠ OPEN — browser-only bootstrap gap.** The terminal units live in the repo, so they only start after `box-source-fetch` clones, which needs the builder's GDS token on the box. A pure browser-only builder has no first-boot way to place that token → terminal never comes up for them (works once a token is seeded via SSH/operator). Fix: bring ttyd up at first boot independent of the clone (cloud-init writes the unit inline). Tracked as a follow-up.
76
+
77
+ ---
78
+
79
+ ## Spike findings (2026-06-28, task 852)
80
+
81
+ **The spike question:** *"the browser-terminal can't run interactive `claude` because Ink (Claude Code's TUI) needs a raw-mode TTY that ttyd's pipe can't provide."*
82
+
83
+ **Verdict: the premise is false. ttyd is NOT a pipe — it is a real PTY, and interactive `claude` (the Ink TUI, including raw mode) runs in it. This is the path we shipped and run in production today.** The "can't run interactive claude" symptoms that motivated this spike were real, but they were misdiagnosed: every one of them traced to something *other* than raw-mode capability, and each already has a landed fix. Below is the root-cause walk so this doesn't get re-investigated.
84
+
85
+ ### Root cause — why "Ink needs raw-mode, ttyd can't provide it" is wrong
86
+
87
+ `ttyd` does not pipe stdio. For each browser connection it allocates a **real kernel pseudo-terminal** via `forkpty()` and runs the login shell as the PTY's session leader. A kernel PTY is exactly the device on which `termios` raw mode (`cfmakeraw` / `ECHO`/`ICANON` cleared) is defined — it is the *same* mechanism a local `xterm` or `ssh` session uses. The browser side (xterm.js over a websocket) is the *display*; the program on the box still talks to a genuine `/dev/pts/N`. So:
88
+
89
+ - `isatty(0/1) === true` inside the ttyd shell, and a program that calls `tcsetattr` to enter raw mode succeeds.
90
+ - Ink (which puts the terminal in raw mode for keypress-by-keypress input) therefore works — there is nothing about ttyd's transport that prevents it.
91
+
92
+ A genuine pipe (`cmd | claude`, or a `systemd` service with no controlling terminal) is the case where raw mode fails — `ENOTTY`, no `tcsetattr`. **That is the headless `claude rc` case ADR 0038 originally killed, not the ttyd case.** The spike title conflated the two: the *headless service* had no PTY; the *ttyd terminal a human sits at* has a full one. ADR 0040 already recorded the live confirmation that plain interactive `claude` runs in the ttyd terminal on the prod box.
93
+
94
+ ### What actually broke "interactive claude in the browser terminal" — and the real fixes (all landed)
95
+
96
+ The symptoms people read as "the TUI won't run / freezes / refuses to start" were these, none of them raw-mode:
97
+
98
+ 1. **`claude` refused to start as root (looked like "TUI won't launch").** The box's `defaultMode=bypassPermissions` (task 1251) makes Claude Code refuse to run as root unless `IS_SANDBOX=1`. The droplet *is* an isolated sandbox. **Fix (task 1314):** `Environment=IS_SANDBOX=1` pinned for the whole ttyd process tree in `infra/box-terminal.service`.
99
+ 2. **The TUI froze mid-session (looked like "raw-mode input is dead").** Claude Code's own startup `git` (and any git a hook spawns) hits the token-less `https://github.com` origin of the rank-gated `/workspace` clone, and `git` opens `/dev/tty` for a `Username for 'https://github.com':` prompt *even with stdin redirected* — grabbing the ttyd PTY and hanging the Ink UI until killed. **Fix (task 854):** `Environment=GIT_TERMINAL_PROMPT=0` for the ttyd tree, so the remote-touching git fails fast instead of prompting (credentialed paths — `box-source-fetch`, ship — are unaffected).
100
+ 3. **`/remote-control` (the next step after a working TUI) reported "Workspace not trusted."** This is the *standalone* `claude rc` shell command's trust mis-check ([anthropics/claude-code#35817](https://github.com/anthropics/claude-code/issues/35817)), not a ttyd or raw-mode problem. **Fix (ADR 0040):** start Remote Control with the in-session **`/remote-control` slash command** from inside an already-trusted `claude`, never the standalone `claude rc`.
101
+ 4. **Session died when the tab closed (looked like an unstable terminal).** ttyd SIGHUPs its child on disconnect, killing a bare login shell + its `claude`. **Fix (task 893):** the launcher wraps the shell in a `tmux new-session -A` "otb" session, so a disconnect only detaches the client; the running `claude` survives on the box and the next connect reattaches.
102
+
103
+ ### What was tried / ruled out
104
+
105
+ - **Ruled out: "ttyd transport can't carry raw mode."** Contradicted by the PTY mechanism above and by the live prod runs recorded in ADR 0040 and codified in `infra/box-terminal.service` (which the box has run in production through park/wake cycles). No raw-mode-specific failure was ever reproduced once the four issues above were fixed.
106
+ - **Ruled out: switching terminal daemons (gotty / wetty / xterm.js-over-a-different-ws).** They all use the same `forkpty()` PTY model; swapping would not change raw-mode behaviour and would throw away the credential-race / tmux-persistence / first-boot hardening already baked into the ttyd unit. No reason to migrate.
107
+ - **Confirmed working path:** ttyd → real PTY → `tmux` "otb" → `claude` (auto-launched by `otb-box-launch`) → interactive Ink TUI → `/remote-control` to hand off to the Claude web/mobile app (ADR 0040). This is the shipped browser-only on-ramp.
108
+
109
+ ### Recommended next step
110
+
111
+ **No infra change. Accept and document that interactive `claude` works in the ttyd terminal — the "raw-mode" framing of task 852 is closed as a misdiagnosis.** The four real issues are already fixed in `infra/box-terminal.service` and ADR 0040; this section is the durable record so the false "Ink needs a raw-mode TTY ttyd can't provide" hypothesis is not re-opened. The only genuinely open terminal item remains the **browser-only bootstrap gap** noted just above (ttyd coming up before the GDS token is on the box), which is independent of raw mode and tracked separately.
@@ -0,0 +1,49 @@
1
+ # ADR 0039 — Setup-first onboarding UX: two-config selector + action-only Path checklist
2
+
3
+ **Date:** 2026-06-06
4
+ **Context:** GDS-V3, task [#777](https://example.com/builders#/task/777). Feeds done-when criterion **C1** (`a newcomer onboards in ≤ 2h`). Builds on [ADR 0035](<redacted>.md) (three onboarding paths), [ADR 0038](<redacted>.md) (the box-hosted ttyd browser terminal, now hardware-verified under [#771](https://example.com/builders#/task/771)), [ADR 0033](<redacted>.md) (the Discord OAuth account-link + auto-join), and [ADR 0034](<redacted>.md)/[#692](https://example.com/builders#/task/692) (the 3-ship xenos→thetes promotion).
5
+ **Status:** Accepted. Implemented this session under [#777](https://example.com/builders#/task/777).
6
+ **Track:** `internal` (dev-system / builder onboarding).
7
+
8
+ ## Problem
9
+
10
+ The builders'-hall onboarding (the "Welcome to the Hall" panel + the "Path into the City" checklist) had two faults for a **zero-technical** newcomer who **won't read paragraphs**:
11
+
12
+ 1. **Setup and milestones were tangled.** The welcome panel mixed *how to get a working environment* (install Claude Code, clone the repo, run `/builder-setup`) with *what to do next*, in dense prose. It also still told newcomers to clone a repo locally — but post-[ADR 0035](<redacted>.md) a Xenos is **box-only** and never clones anything to their own machine.
13
+ 2. **The first checklist step was redundant.** "Bear the city's seal" (GitHub sign-in) is already done — a builder signed in with GitHub to even see the page.
14
+
15
+ ## Decision
16
+
17
+ **Show ONLY setup first, as a two-config selector; then a short, action-only orientation checklist.**
18
+
19
+ ### 1. Setup screen — pick how you connect to your dev box
20
+
21
+ Both configs reach the SAME hosted dev box (Xenos are box-only, [ADR 0035](<redacted>.md)); the choice is only the *client*:
22
+
23
+ - **Claude Desktop app (recommended)** — install the desktop app, open it on an empty folder, then paste a single instruction telling Claude to **read the hosted connect guide** (`GET /api/gds/box/connect`) and follow it. That guide (already served, public, no secrets) runs the OS-appropriate `box-connect` bootstrap (device-flow GitHub sign-in + SSH key + Desktop connection file). Then connect via *Code → Environment → "Example Dev Box"*.
24
+ - **Claude Online (browser only)** — no install; the hall **delivers the box terminal automatically**: one "Set up my dev box" button calls `POST /box/ensure` (auto-provision/wake; [#701](https://example.com/builders#/task/701)) and polls `GET /box/terminal`, then shows the ttyd URL + password ([ADR 0038](<redacted>.md)) right in the panel. Open it and type `claude`. No SSH, no `#support` relay.
25
+
26
+ **Why the hosted guide (not "type a `/builder-*` command"):** a brand-new builder has no repo and therefore none of the project's `/builder-*` skills locally. Pointing Claude at the public guide URL is self-contained — Claude fetches it and runs the bootstrap with nothing pre-installed. The bootstrap self-authenticates via device flow, so it does **not** require `/builder-setup` first.
27
+
28
+ Steps are short, scannable, and plain-spoken (the Mediterranean theme stays in the headings). The repo-clone walkthrough — and the `/public/repo-info` fetch that fed it — is removed from the newcomer panel. Getting the box itself is requested in Discord `#support` (an Archon provisions it — the [ADR 0035](<redacted>.md) §2 first-box cost gate).
29
+
30
+ ### 2. "The Path into the City" — orientation, action-only, all auto-detected
31
+
32
+ Four displayed steps: **Join the Discord server** → **Choose thy craft** → **Run `/builder-setup`** → **Ship three works — and rise to Thetes**. The redundant GitHub-sign-in step is dropped from display, as are `first_start`/`first_claim` (implied by shipping). **Every step auto-detects — no manual ticking** ("the city watches"):
33
+
34
+ - **join_discord** clears when the builder links Discord. The button runs the **existing** OAuth flow (`GET /api/gds/auth/discord/start`, [ADR 0033](<redacted>.md)) — same control as `/builders/settings` — which auto-joins the guild + assigns the rank role; `discord.linked` then reads true. No new Discord backend.
35
+ - **cli_setup** clears once the builder has authenticated the GDS CLI — i.e. run `/builder-setup` (or the box-connect bootstrap); both mint a `source='cli'` session. Detected via `db.hasCliSession` (an `EXISTS` over `builder_sessions`); no manual mark.
36
+ - **pick_disciplines** / **ship_three** are the existing engine-backed steps (craft picker; 3-ship counter).
37
+
38
+ ### 3. Display layer split from the graduation engine
39
+
40
+ The displayed list is a **read-only view** computed by `shapeForApi(state, { shipCount, discordLinked, cliAuthed })` — it does NOT change the onboarding state machine. The engine `STAGES` array (`auth_complete`…`ship_three`) is untouched, so graduation — and therefore the 3-ship xenos→thetes promotion ([#692](https://example.com/builders#/task/692)) and the graduation broadcast — stays driven **solely by ship-count**, independent of the displayed steps. `join_discord`/`cli_setup` are derived from live signals already available server-side (`discord.linked`, a `source='cli'` session) — **no new column, no migration, no write endpoint**.
41
+
42
+ ## Consequences
43
+
44
+ - A newcomer sees setup first, picks a config, follows 3–4 plain steps to a working `claude` session, then a 4-step orientation path that ticks itself as they go.
45
+ - No new tables/columns/endpoints: both new steps are pure derivations in `GET /api/gds/me` (and the `PATCH /me/disciplines` echo). `db.hasCliSession(builderId)` is the only new helper.
46
+ - **The only thing that gates the rank-up is shipping three works.** The orientation steps are informational; a builder is never blocked by them.
47
+ - Connect is bootstrapped from the **hosted `GET /api/gds/box/connect` guide** so a builder with no local repo/skills can still onboard (Claude fetches + runs it).
48
+ - **Config B self-serves the terminal in the hall** (`POST /box/ensure` → poll `GET /box/terminal` → show URL + password) — the `#support` relay is gone. A newcomer's first box still waits for a quick Archon approval (ADR 0035 §2 cost gate), surfaced as a "waiting for approval" state. The remaining hardware dependency ([#774](https://example.com/builders#/task/774)) is the box's terminal reporter publishing its URL on a bare first boot; once it does, the hall shows it automatically.
49
+ - The "Join the Discord" button is suppressed when Discord OAuth isn't configured server-side (`discord.configured` false).