@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,889 @@
1
+ // src/bongos/serve-internal.js — the shared INTERNAL web surface (ADR 0062 R51, task 1190).
2
+ //
3
+ // Mounts every non-game surface the platform serves onto an Express app:
4
+ // * /healthz + /version — liveness + deployed-build info
5
+ // * /api/gds/* — the GDS API (buildGdsRouter)
6
+ // * /downloads/devbox/* — Dev Box installer redirect surface (#904)
7
+ // * generic module web-surface — each enabled module's contributes.webSurfaces
8
+ // hosts served on its Host prefix by ONE generic static
9
+ // loop (the status dashboard at status.<apex>,
10
+ // BV1.R84). The hall declares a webSurface too
11
+ // but is partitioned OUT of this loop — it has
12
+ // the dedicated richer serving below.
13
+ // * the builders' hall host — modules/hall-ui/public/ (builders.<apex>, the
14
+ // hall-ui module's web surface carved in BV1.R85)
15
+ // + the apex /builders/* tree, with the subdomain
16
+ // shim, the Archon/citizen page gates, the
17
+ // asset-stamped HTML, and the primer/diagram
18
+ // static mounts.
19
+ //
20
+ // WHY THIS MODULE EXISTS: the game boot (server.js) and the GDS-only boot
21
+ // (src/platform-server.js, R51) must serve these surfaces IDENTICALLY. Rather
22
+ // than copy-paste the ~330 lines of host routing into both — which would drift
23
+ // the moment one entrypoint changed — both call mountInternalSurfaces(app). This
24
+ // mirrors the registerGameRooms() pattern that keeps server.js and
25
+ // preview-server.js from drifting on the game-room list (ADR 0052).
26
+ //
27
+ // CORE-SAFE: this file lives under src/bongos/ and so is held to the core↔host
28
+ // import boundary (scripts/gds/fitness.js §1) — it imports nothing from the host
29
+ // game runtime (no src/db.js, no src/rooms/, no src/world/, no Colyseus). It
30
+ // serves only static files + the GDS router, so a non-game Cloud Bongos instance
31
+ // runs it unchanged. (The /downloads/devbox/* surface is an instance feature that
32
+ // R54's module system, task 1193, will gate per-instance; it is benign on a
33
+ // vanilla boot — it only redirects to whatever config/devbox-app.json names.)
34
+
35
+ const path = require('path');
36
+ const fs = require('fs');
37
+ const express = require('express');
38
+
39
+ const { buildGdsRouter } = require('./routes');
40
+ const { attachFail } = require('./middleware/error-envelope');
41
+ const metrics = require('./metrics');
42
+ const gdsAuth = require('./auth');
43
+ const { buildStampedHtml } = require('../asset-version');
44
+ const { buildInfo } = require('../build-info');
45
+ const { buildDevboxDownloads } = require('../devbox-downloads');
46
+ const { buildBongosDownloads } = require('../bongos-downloads');
47
+ const { clientBranding, branding } = require('../branding');
48
+ const { clientModules, isModuleEnabled } = require('../modules');
49
+ const { hallWidgetScripts, moduleWebSurfaces } = require('../module-loader/loader');
50
+ const { resolveCoreRoot } = require('../instance-config');
51
+ const { API_PREFIX, LEGACY_API_PREFIXES, API_VERSION, VERSIONED_API_PREFIX, ALL_API_PREFIXES } = require('./api-prefix');
52
+
53
+ // ADR 0108 §1: every use of ROOT below (infra/ installers, modules/, docs/) is
54
+ // core package content. resolveCoreRoot() returns the repo root today, so this
55
+ // is byte-identical for the live instance.
56
+ const ROOT = resolveCoreRoot();
57
+
58
+ // Instance-only RELEASE config (config/devbox-app.json, config/bongos-cli.json) is
59
+ // boundary-excluded from the portable core (publish-manifest INSTANCE_EXCLUDE), so a
60
+ // vanilla self-host boot ships without it. Requiring it hard used to CRASH the boot
61
+ // (task 1824). Guard the require so an absent config makes its download surface simply
62
+ // not mount (fail-to-vanilla, like branding/modules) rather than take the server down.
63
+ // A malformed config (JSON parse error, etc.) still throws — only a MISSING file is
64
+ // tolerated. A branded instance that ships the config gets the routes exactly as before.
65
+ function tryRequireConfig(relPath) {
66
+ try { return require(relPath); }
67
+ catch (e) { if (e && e.code === 'MODULE_NOT_FOUND') return null; throw e; }
68
+ }
69
+
70
+ // Map the pack's camelCase theme color keys → the kebab-case CSS custom
71
+ // properties the hall/status stylesheets read (public-{builders,status}/style.css
72
+ // :root). R57 (task 1196).
73
+ const COLOR_VARS = {
74
+ bg: '--bg', bgCard: '--bg-card', bgDeep: '--bg-deep', bgFrame: '--bg-frame',
75
+ ink: '--ink', inkSoft: '--ink-soft', inkFaint: '--ink-faint',
76
+ rule: '--rule', ruleSoft: '--rule-soft',
77
+ accent: '--accent', accentDeep: '--accent-deep', sea: '--sea', terracotta: '--terracotta',
78
+ };
79
+
80
+ // Build the THEME markup for a served page from the client-safe pack's theme:
81
+ // a <style> overriding the :root palette + font-family vars, the web-font <link>s,
82
+ // and the favicon <link>. Returns '' when the pack carries no theme (so a pack
83
+ // without a `theme.ui` block is a no-op and the stylesheet's own defaults stand).
84
+ // The <style> is emitted so it lands AFTER the page's own stylesheet <link>
85
+ // (which sits earlier in <head>) — equal specificity, later wins — making the
86
+ // pack authoritative over style.css. For OTB the values equal style.css's
87
+ // parchment defaults, so the render is byte-identical; a neutral instance gets
88
+ // its plain palette + system fonts. R57 (task 1196, ADR 0062 §3).
89
+ function themeMarkup(theme) {
90
+ if (!theme) return '';
91
+ const decls = [];
92
+ for (const [key, cssVar] of Object.entries(COLOR_VARS)) {
93
+ const v = theme.colors?.[key];
94
+ if (v) decls.push(`${cssVar}:${v};`);
95
+ }
96
+ if (theme.fonts?.display) decls.push(`--font-display:${theme.fonts.display};`);
97
+ if (theme.fonts?.body) decls.push(`--font-body:${theme.fonts.body};`);
98
+ let out = '';
99
+ for (const href of theme.fonts?.links || []) {
100
+ out += `<link rel="stylesheet" href="${href}">`;
101
+ }
102
+ // `backdrop` paints the page background (e.g. the Cloud Bongos sunrise gradient,
103
+ // ADR 0064 / R74) over the flat var(--bg); omitted packs (OTB) keep --bg.
104
+ const css = [];
105
+ if (decls.length) css.push(`:root{${decls.join('')}}`);
106
+ if (theme.backdrop) css.push(`body{background:${theme.backdrop};}`);
107
+ if (css.length) out += `<style>${css.join('')}</style>`;
108
+ if (theme.favicon) out += `<link rel="icon" href="${theme.favicon}">`;
109
+ return out;
110
+ }
111
+
112
+ // Inject the client-safe branding pack into a served HTML document (R56 task 1195
113
+ // + R57 task 1196, ADR 0062 §3):
114
+ // * `window.__BRANDING__` — a synchronous global so every hall/status script
115
+ // reads instance strings (currency, identity, copy, rank labels) from config
116
+ // with NO fetch, NO race, NO flash-of-default.
117
+ // * the THEME markup — a :root palette/font override <style>, the web-font
118
+ // <link>s, and the favicon <link>, so the LOOK comes from config too.
119
+ // Everything lands at the END of <head>: the script before any deferred page
120
+ // script runs, and the theme <style>/favicon AFTER the page's own stylesheet so
121
+ // the pack wins. Applied to the boot-time stamped HTML maps (the pack is a
122
+ // boot-time singleton, computed once). Off-box surfaces that can't be
123
+ // server-injected (the GitHub-Pages status page) read the same projection from
124
+ // GET /api/gds/public/branding. Pure + exported for the unit test.
125
+ function injectBrandingGlobal(html, brand = clientBranding(), mods = clientModules()) {
126
+ // window.__MODULES__ — the enabled feature-module set (R54 task 1193, ADR 0062
127
+ // §4), injected the same way as __BRANDING__ so hall/status scripts can hide
128
+ // UI sections for off modules synchronously (no fetch, no race). R60 wires the
129
+ // actual section-hiding; this exposes the data.
130
+ const stamped = applyBrandTokens(html, brand);
131
+ const tag = `${clientBundleScriptTag()}<script>window.__BRANDING__=${JSON.stringify(brand)};window.__MODULES__=${JSON.stringify(mods)};</script>${themeMarkup(brand.theme)}`;
132
+ return stamped.includes('</head>') ? stamped.replace('</head>', `${tag}</head>`) : tag + stamped;
133
+ }
134
+
135
+ // Server-side stamp of instance-identity TOKENS into served HTML (task 1705, ADR
136
+ // 0062 §3). A page's static markup carries neutral placeholders instead of a
137
+ // hardcoded host brand, and this replaces them at serve time with the instance's
138
+ // own strings — so a non-OTB instance's <title>/copy render its own name, the
139
+ // no-JS path still works (server-rendered, not client-stamped), and the source
140
+ // stays brand-free (the fitness identity gate). Runs BEFORE the __BRANDING__
141
+ // script is inserted; the injected JSON holds resolved values (never tokens), so
142
+ // order is immaterial. Tokens: {{worldName}} {{productName}} {{currency}}
143
+ // (lowercase label) {{Currency}} (Capitalized) {{publicOrigin}} (the apex origin,
144
+ // for the one functional cross-instance link — sourced from the FULL pack's
145
+ // domains, which clientBranding deliberately omits). Pure + exported for the test;
146
+ // `origin` is injectable so the test drives it with zero I/O.
147
+ function applyBrandTokens(html, brand = clientBranding(), origin = safePublicOrigin()) {
148
+ const world = (brand.identity && brand.identity.worldName) || '';
149
+ const product = (brand.identity && brand.identity.productName) || '';
150
+ const cur = (brand.currency && brand.currency.label) || 'credits';
151
+ const Cur = cur ? cur.charAt(0).toUpperCase() + cur.slice(1) : cur;
152
+ return String(html == null ? '' : html)
153
+ .split('{{worldName}}').join(world)
154
+ .split('{{productName}}').join(product)
155
+ .split('{{Currency}}').join(Cur)
156
+ .split('{{currency}}').join(cur)
157
+ .split('{{publicOrigin}}').join(origin || '');
158
+ }
159
+ // The instance apex origin from the full branding pack (domains is server-only,
160
+ // so it never rides clientBranding). Best-effort so a branding hiccup can't crash
161
+ // serving.
162
+ function safePublicOrigin() {
163
+ try { return branding().domains.publicOrigin || ''; } catch { return ''; }
164
+ }
165
+ // Instance product/world name for the API-discovery index (task 1918). Best-effort:
166
+ // a branding hiccup must not break the JSON response.
167
+ function safeProductName() {
168
+ try {
169
+ const b = branding();
170
+ return (b.identity && (b.identity.productName || b.identity.worldName)) || 'Cloud Bongos';
171
+ } catch { return 'Cloud Bongos'; }
172
+ }
173
+
174
+ // Apply injectBrandingGlobal across a whole stamped-HTML map ({ basename: html }).
175
+ function injectBrandingInto(stamped, brand = clientBranding()) {
176
+ const out = {};
177
+ for (const [name, html] of Object.entries(stamped)) out[name] = injectBrandingGlobal(html, brand);
178
+ return out;
179
+ }
180
+
181
+ // The rendered API-docs page (task 1918) — public-docs/index.html wrapped with the
182
+ // instance branding (title/theme/favicon), memoized once at boot like the apex
183
+ // landing. '' when the instance ships no docs page (then /docs falls through to 404).
184
+ let _docsHtml;
185
+ function docsIndexHtml() {
186
+ if (_docsHtml === undefined) {
187
+ try {
188
+ _docsHtml = injectBrandingGlobal(fs.readFileSync(path.join(ROOT, 'public-docs/index.html'), 'utf8'));
189
+ } catch {
190
+ _docsHtml = '';
191
+ }
192
+ }
193
+ return _docsHtml;
194
+ }
195
+
196
+ // The browser-global build of the generated typed client (task 1996 / R09),
197
+ // served at /lib/bongos-client.js so classic <script> web pages can consume the
198
+ // same generated client the CLI uses (window.BongosClient). Read once at boot
199
+ // from clients/bongos-client/bongos-client.global.js — the SAME committed file
200
+ // the freshness test guards, so the browser can never drift from the spec. ''
201
+ // (→ 404) if the build is somehow absent, so a missing file fails loudly per page.
202
+ let _clientBundle;
203
+ function clientBundleJs() {
204
+ if (_clientBundle === undefined) {
205
+ try {
206
+ _clientBundle = fs.readFileSync(path.join(ROOT, 'clients/bongos-client/bongos-client.global.js'), 'utf8');
207
+ } catch {
208
+ _clientBundle = '';
209
+ }
210
+ }
211
+ return _clientBundle;
212
+ }
213
+
214
+ // The <script> tag that loads the generated client on every internal page (task
215
+ // 1996 / R09), content-hashed so a client change busts the browser/Cloudflare
216
+ // cache exactly like the ?v= stamps on the hall's own scripts. Injected into
217
+ // <head> by injectBrandingGlobal so window.BongosClient is defined before any
218
+ // body-end page script runs. '' when the build is absent (pages then have no
219
+ // client and fail loudly on first use, same as a missing script anywhere).
220
+ let _clientBundleTag;
221
+ function clientBundleScriptTag() {
222
+ if (_clientBundleTag === undefined) {
223
+ const js = clientBundleJs();
224
+ _clientBundleTag = js
225
+ ? `<script src="/lib/bongos-client.js?v=${require('node:crypto').createHash('sha256').update(js).digest('hex').slice(0, 8)}"></script>`
226
+ : '';
227
+ }
228
+ return _clientBundleTag;
229
+ }
230
+
231
+ // Inject each ENABLED module's hall-widget client <script> into the hall index HTML
232
+ // (BV1.R75, ADR 0062 §4). The script must load BEFORE builders.js (which mounts the
233
+ // registered widgets) but AFTER hall-widgets.js/dom-utils.js (which define the
234
+ // registry) — so we insert just before the existing builders.js <script>. A disabled
235
+ // module's widget is never injected (it vanishes); contributeHallWidget is the second
236
+ // gate (it no-ops a widget whose module is off). `scripts` is the loader projection
237
+ // [{ key, src }]; default reads the live enabled set. Pure + exported for the test.
238
+ function injectModuleHallWidgets(html, scripts = hallWidgetScripts({ isEnabled: isModuleEnabled })) {
239
+ if (!Array.isArray(scripts) || scripts.length === 0) return html;
240
+ const tags = scripts
241
+ .map((s) => `<script src="${s.src}" defer></script>`)
242
+ .join('');
243
+ // Anchor on builders.js's <script> regardless of its ?v= stamp so the module
244
+ // widgets always land immediately before the consumer that mounts them.
245
+ const anchor = html.match(/<script src="\/builders\/builders\.js[^"]*"[^>]*><\/script>/);
246
+ if (anchor) return html.replace(anchor[0], tags + anchor[0]);
247
+ return html.includes('</body>') ? html.replace('</body>', `${tags}</body>`) : html + tags;
248
+ }
249
+
250
+ // Host-based routing. status.<apex> gets the public dashboard (served by the
251
+ // status-ui MODULE's web surface, BV1.R84 — the generic webSurfaces loop below);
252
+ // builders.<apex> gets the builder surface (served by the hall-ui MODULE's web
253
+ // surface, BV1.R85 — its DEDICATED block below); the apex host gets the game
254
+ // (server.js) or — on a game-less instance — a plain 404 (platform-server.js). All
255
+ // share the same process + the same /api/gds/* surface (mounted host-agnostically
256
+ // below). The prefix checks are apex-agnostic (any domain), so no brand string lives
257
+ // here. isStatusHost + isBuildersHost stay the literal "status."/"builders."
258
+ // predicates the entrypoints (server.js/platform-server.js) use to keep their apex
259
+ // tail off those hosts; inside mountInternalSurfaces the GENERIC isModuleSurfaceHost
260
+ // (any enabled GENERIC web-surface host) is used to skip the status-style surfaces,
261
+ // while the builders host keeps its own isBuildersHost guard (it's served by the
262
+ // richer dedicated block, not the generic loop).
263
+ function isStatusHost(req) {
264
+ const host = (req.headers.host || '').toLowerCase().split(':')[0];
265
+ return host.startsWith('status.');
266
+ }
267
+ // A single-host instance — one with no dedicated builders.<apex> subdomain, e.g. a
268
+ // co-located instance on a one-level subdomain like emersonian-circles.cloudbongos.com
269
+ // (where free-plan TLS can't cover a two-level builders.<sub>.<apex>) — serves the hall
270
+ // on its OWN configured buildersOrigin host. Treating that configured host as a builders
271
+ // host makes the hall, not the apex landing, its front door (so /builders no longer
272
+ // 301s to a builders.<host> that was never provisioned). Back-compat: an instance whose
273
+ // buildersOrigin is the conventional builders.<apex> already matches the
274
+ // startsWith('builders.') branch, so its routing is byte-identical; the second branch
275
+ // only ever fires for an instance that deliberately points buildersOrigin at a
276
+ // non-"builders." host (the configurable-root / hosting-module direction — ADR 0108,
277
+ // tasks 1917/1485).
278
+ function configuredBuildersHost() {
279
+ try {
280
+ const bo = (branding().domains || {}).buildersOrigin || '';
281
+ return bo ? new URL(bo).host.toLowerCase().split(':')[0] : '';
282
+ } catch { return ''; }
283
+ }
284
+ function isBuildersHost(req) {
285
+ const host = (req.headers.host || '').toLowerCase().split(':')[0];
286
+ if (host.startsWith('builders.')) return true;
287
+ const cfg = configuredBuildersHost();
288
+ return cfg !== '' && host === cfg;
289
+ }
290
+
291
+ // Mount the internal web surfaces onto `app`, in the exact order server.js used.
292
+ // Call this ONCE, right after app setup (trust proxy etc.) and BEFORE any
293
+ // entrypoint-specific tail (the game static + themed 404 in server.js; the plain
294
+ // 404 in platform-server.js).
295
+ function mountInternalSurfaces(app) {
296
+ // Prometheus instrumentation (task #1680, V4.R28a). The counting middleware
297
+ // mounts FIRST so it wraps every surface registered below (and, since
298
+ // mountInternalSurfaces runs before the game host blocks in server.js, the
299
+ // game routes too) — it records on res 'finish' regardless of which handler
300
+ // answers. The /metrics scrape endpoint is access-gated (loopback Prometheus
301
+ // or Bearer METRICS_TOKEN; see metrics.authorizeScrape).
302
+ app.use(metrics.httpMiddleware());
303
+ app.get('/metrics', metrics.metricsHandler());
304
+
305
+ // res.fail() decorator at the APP level (BV1 R04 / task 1991 / ADR 0116). The
306
+ // GDS router mounts its own attachFail, but a few app-level routes here (the
307
+ // /downloads/* redirect surface) also emit the error envelope via res.fail, and
308
+ // they run OUTSIDE that router — so decorate every response here too. Idempotent
309
+ // with the router's copy (it just re-installs the same method).
310
+ app.use(attachFail);
311
+
312
+ app.get('/healthz', (_req, res) => {
313
+ res.type('text/plain').send('ok');
314
+ });
315
+
316
+ // Deployed build info (#858, ADR 0042). Public + read-only — exposes the git
317
+ // commit the running process was deployed from, so ship.js's ci-mode deploy
318
+ // confirmation can commit-PIN (deployedCommit === mergeCommit) rather than only
319
+ // checking liveness via /healthz. Low-sensitivity (a public commit sha).
320
+ app.get('/version', (_req, res) => {
321
+ res.json(buildInfo());
322
+ });
323
+
324
+ // Generated typed client for classic <script> web pages (task 1996 / R09).
325
+ // Host-agnostic and registered early so it wins over the host-routed surface
326
+ // middleware; every internal surface (hall, status) can load window.BongosClient
327
+ // from this one path. A short max-age + the page-side ?v= query bust the cache
328
+ // on client updates (the bytes are content-stable per deploy).
329
+ app.get('/lib/bongos-client.js', (_req, res) => {
330
+ const js = clientBundleJs();
331
+ if (!js) return res.status(404).type('text/plain').send('// bongos-client bundle not found on the server');
332
+ res.type('application/javascript');
333
+ res.set('Cache-Control', 'public, max-age=300');
334
+ res.send(js);
335
+ });
336
+
337
+ // GDS API (game development system; renamed from PMS in V3.R01). See
338
+ // docs/adr/0010-pms-architecture.md and limitations/pms-v1-shipped.md for
339
+ // the historical context (when the surface was still /api/pms).
340
+ //
341
+ // The transitional /api/pms/* → /api/gds/* redirect surface was removed in
342
+ // V3.R01-followup (#308, 2026-05-13) after a repo audit confirmed no caller
343
+ // still hits the legacy path. Read-side back-compat for the `pms_session`
344
+ // cookie, the `PMS_GITHUB_*` env vars, and the `~/.config/otb/pms-session.json`
345
+ // file path continues to live in src/bongos/auth.js + scripts/gds/cli-lib.js —
346
+ // those are independent of the URL surface and protect existing local
347
+ // sessions during the rollout.
348
+ // Machine-discovery index (task 1918): a bare GET on the API root answers with a
349
+ // small JSON map to the contract (the rendered docs + the OpenAPI spec) instead
350
+ // of a 404 — so an AI agent or a curious client that hits the base path finds the
351
+ // reference on its own. CORS-open + no auth (it names only public locations).
352
+ //
353
+ // Dual-mount (task 1919): the router + this discovery index are mounted at the
354
+ // canonical API_PREFIX (/api/bongos) AND every LEGACY_API_PREFIXES entry
355
+ // (/api/gds) — the legacy alias stays FOREVER so shipped Dev Box binaries, the
356
+ // cached status mirror, and live boxes keep working (src/bongos/api-prefix.js). The
357
+ // SAME router instance is mounted at each path. Each discovery GET is registered
358
+ // BEFORE its router mount so the exact base-path GET wins; every other
359
+ // method/subpath falls through to the router.
360
+ const gdsRouter = buildGdsRouter();
361
+ const discoveryIndex = (req, res) => {
362
+ res.set('Access-Control-Allow-Origin', '*');
363
+ res.set('Cache-Control', 'public, max-age=300');
364
+ // Docs-discovery contract (ADR 0114): advertise the instance's doc surfaces —
365
+ // the hall (canonical doc-reading surface), the status page (outage fallback),
366
+ // and the public origin — sourced from branding().domains, so a second instance
367
+ // resolves its own surfaces with no hard-coded host. null when unset.
368
+ const domains = (() => { try { return branding().domains || {}; } catch { return {}; } })();
369
+ res.json({
370
+ name: `${safeProductName()} — Bongos API`,
371
+ api_base: API_PREFIX, // unversioned canonical alias — always resolves to the current version, never breaks
372
+ api_version: API_VERSION, // the explicit CONTRACT version (ADR 0117)
373
+ api_base_versioned: VERSIONED_API_PREFIX, // '/api/bongos/v1' — pin here for a stable contract
374
+ docs: '/docs',
375
+ openapi: '/docs/openapi.json',
376
+ reference: 'docs/api-reference.md',
377
+ hall: domains.buildersOrigin || null,
378
+ status: domains.statusOrigin || null,
379
+ public: domains.publicOrigin || null,
380
+ ranks: ['public', 'any-builder', 'metic+archon', 'archon'],
381
+ note: 'Ranks are enforced server-side per request (ADR 0016). Open /docs for the full reference. The unversioned base + /api/gds are permanent aliases pinned to the current version (ADR 0117).',
382
+ });
383
+ };
384
+ // Mount the router + discovery index at EVERY prefix, most-specific first
385
+ // (ALL_API_PREFIXES: /api/bongos/v1, then /api/bongos, then /api/gds) so the
386
+ // versioned path is matched before the bare one (BV1 R06 / ADR 0117). The SAME
387
+ // router serves all three — the unversioned + legacy paths are permanent aliases
388
+ // pinned to v1.
389
+ for (const prefix of ALL_API_PREFIXES) {
390
+ app.get(prefix, discoveryIndex);
391
+ app.use(prefix, gdsRouter);
392
+ }
393
+
394
+ // Dev Box desktop app downloads (#904, ADR 0045). Branded redirect surface:
395
+ // the installers live as release assets on the public releases repo (they
396
+ // can't ride the git deploy — see src/devbox-downloads.js), and these routes
397
+ // keep every builder-facing URL on the apex. Mounted right after the API and
398
+ // BEFORE the host shims so the same paths work on the apex AND on
399
+ // builders.<apex> (the builders rewrite shim below only prefixes non-/api
400
+ // paths, so without this placement /downloads/* would be rewritten to
401
+ // /builders/downloads/* and 404).
402
+ const devboxCfg = tryRequireConfig('../../config/devbox-app.json');
403
+ if (devboxCfg) {
404
+ const devboxDownloads = buildDevboxDownloads(devboxCfg);
405
+ app.get('/downloads/devbox/latest.json', (_req, res) => {
406
+ // Short TTL: a release bump (config change + deploy) is visible within a
407
+ // minute, same posture as the hall's HTML.
408
+ res.set('Cache-Control', 'public, max-age=60');
409
+ res.json(devboxDownloads.latestJson());
410
+ });
411
+ // #924: the lightweight update-check endpoint the Dev Box app polls + the hall
412
+ // /settings courtesy line reads. MUST be registered before the `:target` route
413
+ // below, or Express would treat "version" as a download target and 404 it.
414
+ app.get('/downloads/devbox/version', (_req, res) => {
415
+ res.set('Cache-Control', 'public, max-age=60');
416
+ res.json(devboxDownloads.versionJson());
417
+ });
418
+ app.get('/downloads/devbox/:target', (req, res) => {
419
+ const url = devboxDownloads.redirectFor(req.params.target);
420
+ if (!url) return res.fail('unknown_target', 404, { targets: Object.keys(devboxDownloads.latestJson().assets) });
421
+ res.set('Cache-Control', 'public, max-age=60');
422
+ res.redirect(302, url);
423
+ });
424
+ }
425
+
426
+ // Standalone `bongos` CLI downloads + the one-line installer (task 1248,
427
+ // V4.R81). Same off-repo release pattern as the Dev Box block above: the
428
+ // compiled per-platform binaries live as release assets on the public releases
429
+ // repo (config/bongos-cli.json → src/bongos-downloads.js), and these routes keep
430
+ // every user-facing URL on the apex. The installer scripts are served with the
431
+ // server's PINNED origin substituted for __GDS_API_BASE__ — never a request
432
+ // header, so a spoofed Host can't redirect a `curl | sh` (the box-connect.sh rule).
433
+ const bongosCfg = tryRequireConfig('../../config/bongos-cli.json');
434
+ if (bongosCfg) {
435
+ const bongosDownloads = buildBongosDownloads(bongosCfg);
436
+ // Pinned public origin for the served installer scripts (never a request header —
437
+ // a spoofed Host can't redirect a `curl | sh`). The fallback comes from the
438
+ // branding pack, not a hardcoded host brand (ADR 0062 §3).
439
+ const BONGOS_PUBLIC_ORIGIN = process.env.GDS_PUBLIC_ORIGIN || branding().domains.publicOrigin;
440
+ const readBongosInstallScript = (name) => {
441
+ try {
442
+ return fs.readFileSync(path.join(ROOT, 'infra', name), 'utf8').split('__GDS_API_BASE__').join(BONGOS_PUBLIC_ORIGIN);
443
+ } catch { return null; }
444
+ };
445
+ app.get('/downloads/bongos/latest.json', (_req, res) => {
446
+ res.set('Cache-Control', 'public, max-age=60');
447
+ res.json(bongosDownloads.latestJson());
448
+ });
449
+ app.get('/downloads/bongos/version', (_req, res) => {
450
+ res.set('Cache-Control', 'public, max-age=60');
451
+ res.json(bongosDownloads.versionJson());
452
+ });
453
+ // The one-line installers — text/plain so `curl | sh` and `irm | iex` receive
454
+ // raw script bytes. Registered BEFORE the :target catch-all so their literal
455
+ // paths win (Express matches in registration order).
456
+ app.get('/downloads/bongos/install.sh', (_req, res) => {
457
+ const script = readBongosInstallScript('bongos-install.sh');
458
+ if (script == null) return res.status(404).type('text/plain').send('# bongos-install.sh not found on the server');
459
+ res.type('text/plain').send(script);
460
+ });
461
+ app.get('/downloads/bongos/install.ps1', (_req, res) => {
462
+ const script = readBongosInstallScript('bongos-install.ps1');
463
+ if (script == null) return res.status(404).type('text/plain').send('# bongos-install.ps1 not found on the server');
464
+ res.type('text/plain').send(script);
465
+ });
466
+ app.get('/downloads/bongos/:target', (req, res) => {
467
+ const url = bongosDownloads.redirectFor(req.params.target);
468
+ if (!url) return res.fail('unknown_target', 404, { targets: Object.keys(bongosDownloads.latestJson().assets) });
469
+ res.set('Cache-Control', 'public, max-age=60');
470
+ res.redirect(302, url);
471
+ });
472
+ }
473
+
474
+ // Module-contributed HOST-ROUTED web surfaces (BV1.R84, ADR 0093 §1 /
475
+ // 02-module-map §6). A module that owns a web surface declares
476
+ // contributes.webSurfaces:[{ host, dir }] and ships the static dir at
477
+ // modules/<key>/<dir>/; the loader's moduleWebSurfaces() resolves the enabled set
478
+ // to [{ key, host, dir(abs) }]. This GENERALIZES the former hardcoded status-host
479
+ // block (the surface that moved to modules/status-ui/ in BV1.R84): serve-internal
480
+ // serves each surface on its Host prefix with the SAME machinery the status page
481
+ // always used — the in-memory content-hash-
482
+ // stamped index (#521, defeats Cloudflare's 4h TTL) + the per-instance branding/
483
+ // theme injection — and falls through to an on-disk static handler for assets.
484
+ // The status dashboard (modules/status-ui/, host "status.") is served here; a
485
+ // disabled module's surface is never built here, so its host routes nowhere
486
+ // (server.js/platform-server.js then 404 it on the apex tail). No module key is
487
+ // named — the kernel stays module-agnostic (ADR 0083 §Decision #4).
488
+ //
489
+ // The HALL (modules/hall-ui/, host "builders.", BV1.R85) is itself a declared
490
+ // web-surface module, but it is NOT served by this GENERIC static loop — it has
491
+ // far richer serving (the apex /builders/* shim, the canonical-URL redirects,
492
+ // the citizen/Archon page gates, asset-stamping per page, and the module
493
+ // hall-widget injection) in its DEDICATED block below. So we PARTITION the
494
+ // resolved set on the builders-host predicate: the generic loop owns every
495
+ // surface EXCEPT the hall; the dedicated block owns the hall. The filter is on
496
+ // the host (isBuildersHost), not the "hall-ui" key, so the kernel still names no
497
+ // module key — a future surface that wants the generic treatment just declares a
498
+ // non-builders host. (The webSurfaces manifest declaration still makes the hall a
499
+ // loader-DISCOVERED module — it feeds the scope map, the fitness keys, docs, and
500
+ // window.__MODULES__; it is the dedicated serving below that reads the bytes.)
501
+ const buildersHostReq = (host) => ({ headers: { host } });
502
+ const allWebSurfaces = moduleWebSurfaces({ isEnabled: isModuleEnabled });
503
+ const webSurfaces = allWebSurfaces
504
+ .filter((s) => !isBuildersHost(buildersHostReq(s.host))) // hall served by the dedicated block, not here
505
+ .map((s) => ({
506
+ key: s.key,
507
+ host: s.host,
508
+ static: express.static(s.dir, { extensions: ['html'], maxAge: '5m' }),
509
+ // Stamp + brand the index in memory at boot (a boot-time singleton, like the
510
+ // hall's). urlPrefix '/' because the surface is served at the host root.
511
+ html: injectBrandingInto(buildStampedHtml(s.dir, '/')),
512
+ }));
513
+ // True iff this request's Host matches a GENERIC module web surface (status-style,
514
+ // served by the loop below) — replaces the literal isStatusHost() guard the
515
+ // /builders middleware used, so those guards skip every generic surface host with
516
+ // zero hardcoded host string. The hall's own host is deliberately EXCLUDED (it was
517
+ // partitioned out above), so the dedicated /builders blocks below do NOT self-skip
518
+ // the builders host — they are what serves it.
519
+ function isModuleSurfaceHost(req) {
520
+ const host = (req.headers.host || '').toLowerCase().split(':')[0];
521
+ return webSurfaces.some((s) => host.startsWith(s.host));
522
+ }
523
+
524
+ app.use((req, res, next) => {
525
+ const host = (req.headers.host || '').toLowerCase().split(':')[0];
526
+ const surface = webSurfaces.find((s) => host.startsWith(s.host));
527
+ if (!surface) return next();
528
+ // /api/* already handled above; everything else on a surface host serves from
529
+ // the module's static dir. The index page comes from the stamped in-memory copy
530
+ // (asset ?v= = content hash); everything else falls through to the on-disk
531
+ // static handler.
532
+ if ((req.method === 'GET' || req.method === 'HEAD') &&
533
+ (req.path === '/' || req.path === '/index.html') &&
534
+ surface.html['index.html']) {
535
+ res.set('Cache-Control', 'public, max-age=60');
536
+ res.type('html');
537
+ return res.send(surface.html['index.html']);
538
+ }
539
+ return surface.static(req, res, next);
540
+ });
541
+
542
+ // builders.<apex> canonical-URL redirect (#744). On the subdomain the
543
+ // /builders prefix is redundant — the page lives at the root. Old links/bookmarks
544
+ // (and the legacy in-page hrefs) point at /builders[/<page>]; 301 them to the
545
+ // clean path so the address bar never shows ".../builders". PAGE paths only —
546
+ // the slug is [a-z0-9-]+ with no dot, so asset URLs the pages emit
547
+ // (/builders/style.css, /builders/*.js, /builders/diagrams/*.png) and the API
548
+ // (/api/gds/builders/*) never match and pass through to the shim below.
549
+ const BUILDERS_CANONICAL_RE = /^\/builders(?:\/([a-z0-9-]+))?\/?$/i;
550
+ app.use((req, res, next) => {
551
+ if (!isBuildersHost(req)) return next();
552
+ if (req.method !== 'GET' && req.method !== 'HEAD') return next();
553
+ const m = req.path.match(BUILDERS_CANONICAL_RE);
554
+ if (!m) return next();
555
+ const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
556
+ return res.redirect(301, `/${m[1] || ''}${qs}`); // /builders → /, /builders/watch → /watch
557
+ });
558
+
559
+ // builders.<apex> rewrite shim (#726). The dedicated builder subdomain serves
560
+ // the SAME surface as the main host's /builders/* tree, just rooted at "/".
561
+ // Rather than duplicate the serving + gating + asset-stamping logic below, this
562
+ // shim rewrites every non-API path on the builders host onto the existing
563
+ // /builders/* path so all of that logic is reused verbatim:
564
+ // GET https://builders.<apex>/ → /builders/ (hall)
565
+ // GET https://builders.<apex>/watch → /builders/watch (Archon)
566
+ // GET https://builders.<apex>/settings → /builders/settings
567
+ // GET https://builders.<apex>/builders/style.css → unchanged
568
+ // Paths already under /builders pass through untouched, so absolute asset URLs
569
+ // the pages emit (/builders/style.css, /builders/*.js) resolve on this host too.
570
+ app.use((req, res, next) => {
571
+ if (!isBuildersHost(req)) return next();
572
+ if (req.path.startsWith('/api/') || req.path === '/healthz') return next();
573
+ if (req.path === '/builders' || req.path.startsWith('/builders/')) return next();
574
+ // Prefix with /builders so the handlers below match. req.url carries the
575
+ // query string; preserve it. '/' → '/builders/', '/watch' → '/builders/watch'.
576
+ req.url = '/builders/' + req.url.replace(/^\/+/, '');
577
+ next();
578
+ });
579
+
580
+ // #739 (ADR 0036 cutover): the builder surface's canonical home is now the
581
+ // subdomain. On the apex/game host, redirect /builders and /builders/* to
582
+ // builders.<apex>/<rest> so old links keep working while the subdomain becomes
583
+ // the front door. Runs ONLY on the apex (skipped on the status + builders
584
+ // hosts; /api is host-agnostic and mounted earlier, and never starts /builders).
585
+ // 301 (permanent) — the cutover is proven live + verified end-to-end (#741;
586
+ // started as a 302 in #739). The URL fragment (#/task/N deep-links) is preserved
587
+ // by the browser across the redirect; the query string is carried explicitly.
588
+ // The redirect target apex is read from the request host (strip a leading
589
+ // "builders."/"status." if present) so it stays brand-agnostic.
590
+ app.use((req, res, next) => {
591
+ if (isModuleSurfaceHost(req) || isBuildersHost(req)) return next();
592
+ if (req.path !== '/builders' && !req.path.startsWith('/builders/')) return next();
593
+ const apex = (req.headers.host || '').toLowerCase().split(':')[0];
594
+ const rest = req.path.replace(/^\/builders\/?/, ''); // '/builders'→'', '/builders/watch'→'watch'
595
+ const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
596
+ return res.redirect(301, `https://builders.${apex}/${rest}${qs}`);
597
+ });
598
+
599
+ // The /watch monitoring page and the /harbor dev-box management page (idea 257)
600
+ // and the /gate page (task 1179) are all Archon-only. Same server-side page
601
+ // gate, same posture: a non-Archon never receives the shell (requireArchonPage
602
+ // redirects a signed-in non-Archon to the hall, an unauthenticated visitor into
603
+ // sign-in). The subdomain shim above has already rewritten the bare
604
+ // /watch + /harbor + /gate onto /builders/*. (Defense in depth: every data
605
+ // endpoint these pages call is independently requireRank('archon').)
606
+ const WATCH_PAGE_RE = /^\/builders\/watch(?:\.html)?\/?$/;
607
+ const HARBOR_PAGE_RE = /^\/builders\/harbor(?:\.html)?\/?$/;
608
+ const GATE_PAGE_RE = /^\/builders\/gate(?:\.html)?\/?$/;
609
+ app.use((req, res, next) => {
610
+ if (isModuleSurfaceHost(req)) return next();
611
+ if (!req.path.startsWith('/builders')) return next();
612
+ const norm = path.posix.normalize(req.path);
613
+ if (WATCH_PAGE_RE.test(norm) || HARBOR_PAGE_RE.test(norm) || GATE_PAGE_RE.test(norm)) {
614
+ return gdsAuth.requireArchonPage(req, res, next);
615
+ }
616
+ next();
617
+ });
618
+
619
+ // The /builders surface — authenticated single-page UI for active builders.
620
+ // Served from the hall-ui module's public/ dir (modules/hall-ui/public/, carved
621
+ // out of the repo-root public-builders/ in BV1.R85). The Caddy reverse-proxy
622
+ // passes the request through; this handler short-circuits before any entrypoint
623
+ // tail so /builders/* does not fall into the game's public/.
624
+ //
625
+ // This is the hall's DEDICATED serving — the richer twin of the generic
626
+ // web-surface loop above (which the hall is partitioned out of): the apex
627
+ // /builders/* shim, the canonical-URL redirects, the citizen/Archon page gates,
628
+ // per-page asset-stamping, and the module hall-widget injection all live here and
629
+ // read from BUILDERS_DIR. The module key is NOT named — the path is the
630
+ // hall-ui module's public dir, resolved once here.
631
+ //
632
+ // Auth is handled client-side: the page calls /api/gds/me; on 401 it redirects
633
+ // the browser to /api/gds/auth/web/start, which (per src/bongos/routes/auth.js)
634
+ // loops back to /builders with a gds_session cookie set.
635
+ const BUILDERS_DIR = path.join(ROOT, 'modules', 'hall-ui', 'public');
636
+ const buildersStatic = express.static(BUILDERS_DIR, {
637
+ extensions: ['html'],
638
+ setHeaders: (res, filePath) => {
639
+ if (/\.(html|js|json)$/.test(filePath)) {
640
+ res.setHeader('Cache-Control', 'public, max-age=60');
641
+ } else {
642
+ res.setHeader('Cache-Control', 'public, max-age=300');
643
+ }
644
+ },
645
+ });
646
+
647
+ // Task #521: content-hash the hall HTML pages' asset ?v= queries at boot, in
648
+ // memory. This is the durable fix for the stale-asset bug — a changed
649
+ // builders.js/style.css gets a new ?v= automatically, so browsers + Cloudflare
650
+ // fetch it fresh instead of serving the pre-deploy bytes for hours. Keyed by
651
+ // basename; the page-serving middleware below maps a request path to a file.
652
+ const buildersHtml = injectBrandingInto(buildStampedHtml(BUILDERS_DIR, '/builders/'));
653
+ // BV1.R75 (ADR 0062 §4): inject each enabled module's hall-widget client script
654
+ // into the hall index page so a module's contributed UI section appears. No-op when
655
+ // no enabled module ships one (the default today). Only the index hall carries the
656
+ // registry-driven <main>, so this targets index.html alone.
657
+ if (buildersHtml['index.html']) {
658
+ buildersHtml['index.html'] = injectModuleHallWidgets(buildersHtml['index.html']);
659
+ }
660
+
661
+ // Serve enabled modules' browser assets at /modules/<key>/public/* — the URL the
662
+ // loader's hallWidgetScripts projection points at. Mounted BEFORE the /builders
663
+ // static handler. Gated per-request on the live enabled set so a disabled module's
664
+ // assets 404 (defense in depth behind the no-injection above). Same Cache-Control
665
+ // posture as the hall's own scripts.
666
+ const MODULE_ASSET_RE = /^\/modules\/([a-z][a-z0-9-]*)\/public\/(.+)$/;
667
+ const MODULES_ROOT = path.join(ROOT, 'modules');
668
+ app.get(MODULE_ASSET_RE, (req, res, next) => {
669
+ if (isModuleSurfaceHost(req)) return next();
670
+ const m = req.path.match(MODULE_ASSET_RE);
671
+ const key = m[1];
672
+ let enabled = false;
673
+ try { enabled = isModuleEnabled(key); } catch { enabled = false; } // unknown key → not enabled
674
+ if (!enabled) return res.status(404).type('text/plain').send('not found');
675
+ // Normalize the asset sub-path to keep it inside modules/<key>/public.
676
+ const rel = path.posix.normalize(m[2]);
677
+ if (rel.startsWith('..') || path.isAbsolute(rel)) return res.status(404).type('text/plain').send('not found');
678
+ res.set('Cache-Control', 'public, max-age=60');
679
+ return res.sendFile(path.join(MODULES_ROOT, key, 'public', rel), (err) => {
680
+ if (err) { if (!res.headersSent) res.status(404).type('text/plain').send('not found'); }
681
+ });
682
+ });
683
+
684
+ // Resolve a /builders/* request path to a stamped HTML basename, mirroring
685
+ // express.static's `extensions: ['html']` resolution. Returns null for any
686
+ // path that isn't one of our HTML pages (assets, unknown paths) so it falls
687
+ // through to the static handler.
688
+ const BUILDERS_PAGE_RE = /^\/builders\/([a-z0-9-]+)(?:\.html)?\/?$/i;
689
+ // task 751: the task-detail page is the one hall page addressed by a SECOND
690
+ // path segment (an id) rather than a bare slug — /builders/task/:id — so it
691
+ // needs its own two-segment match ahead of the single-segment resolution
692
+ // below. Same gate tier as Work/Roadmap/Goals (SIGNEDIN_BUILDERS_PAGE_RE).
693
+ const TASK_DETAIL_PAGE_RE = /^\/builders\/task\/(\d+)\/?$/;
694
+ function buildersHtmlName(reqPath) {
695
+ if (reqPath === '/builders' || reqPath === '/builders/') return 'index.html';
696
+ if (TASK_DETAIL_PAGE_RE.test(reqPath)) return buildersHtml['task.html'] ? 'task.html' : null;
697
+ // e.g. /builders/ranks or /builders/ranks.html → ranks.html
698
+ const m = reqPath.match(BUILDERS_PAGE_RE);
699
+ if (m) {
700
+ const name = `${m[1].toLowerCase()}.html`;
701
+ if (buildersHtml[name]) return name;
702
+ }
703
+ return null;
704
+ }
705
+
706
+ // #488: server-side sign-in gate for the citizen primer + the system-maps.
707
+ // These pages — and their content assets (the primer markdown + the diagram
708
+ // PNGs) — are citizen-only: an unauthenticated request must NOT be served the
709
+ // file. Mounted BEFORE every /builders static handler below so the gate runs
710
+ // first; gdsAuth.requireBuilderPage redirects an unauthenticated request into
711
+ // the web sign-in flow instead of returning the content. This is the real
712
+ // access control (curl-proof / no-JS-proof) — the link-hiding in builders.js
713
+ // is only cosmetic.
714
+ //
715
+ // Scope is deliberately narrow: the hall index (/builders), the shared assets
716
+ // (style.css, *.js, fonts, cursors), the not-ready notice, and the sign-in flow
717
+ // itself stay PUBLIC so the landing card + GitHub button render for a stranger.
718
+ //
719
+ // Two tiers of gate (#770):
720
+ // * NON-XENOS (Thetes+) — the orientation reading rooms (primer, diagrams).
721
+ // A Xenos's path is action-only; an unauth visitor is sent to sign-in, a
722
+ // Xenos to the friendly /builders/not-ready notice.
723
+ // * ANY SIGNED-IN BUILDER — the Work Board, Roadmap, the Goals page, the
724
+ // task-detail page, the Repo Atlas, the Dev Box pair page,
725
+ // and Settings (#748, #904, task 1293, task 1757, task 751): a Xenos IS
726
+ // allowed, but an unauthenticated visitor is bounced into sign-in rather
727
+ // than served the page shell. Settings sits in THIS tier (not the
728
+ // reading-room tier) because a newcomer needs it for first-time setup —
729
+ // CLI re-auth (the "CLI Access" section) and choosing their craft both
730
+ // live there (task 1291). It must still stay behind sign-in: it shows
731
+ // personal prefs and mints CLI tokens.
732
+ // The link-hiding in builders.js is only cosmetic; these are the real gates.
733
+ const NONXENOS_BUILDERS_PAGE_RE =
734
+ /^\/builders\/(?:primer(?:\.md|\.html)?|diagrams(?:\.html)?)\/?$/;
735
+ const NONXENOS_BUILDERS_PREFIX_RE = /^\/builders\/diagrams\//;
736
+ const SIGNEDIN_BUILDERS_PAGE_RE = /^\/builders\/(?:work|pair|settings|atlas|modules|goals|roadmap)(?:\.html)?\/?$/;
737
+ app.use((req, res, next) => {
738
+ if (isModuleSurfaceHost(req)) return next();
739
+ // Cheap early-out: only /builders/* paths can ever be gated, so skip the
740
+ // work entirely for game / API / asset requests.
741
+ if (!req.path.startsWith('/builders')) return next();
742
+ // Match the NORMALIZED path. express.static (send) calls path.normalize on
743
+ // the sub-path before resolving the file, so '/builders//primer' and
744
+ // '/builders/./primer' both serve primer.html — but those raw forms slip
745
+ // past anchored regexes. Normalizing here (collapse '//', resolve './..')
746
+ // closes that bypass so the gate holds at the application layer, not just
747
+ // behind the proxy's path normalization.
748
+ const norm = path.posix.normalize(req.path);
749
+ if (NONXENOS_BUILDERS_PAGE_RE.test(norm) || NONXENOS_BUILDERS_PREFIX_RE.test(norm)) {
750
+ return gdsAuth.requireNonXenosPage(req, res, next);
751
+ }
752
+ if (SIGNEDIN_BUILDERS_PAGE_RE.test(norm) || TASK_DETAIL_PAGE_RE.test(norm)) {
753
+ return gdsAuth.requireBuilderPage(req, res, next);
754
+ }
755
+ next();
756
+ });
757
+
758
+ // V3.R13-followup-2 (#411): /builders/diagrams/ (with trailing slash) used to
759
+ // fall through to the game's catch-all because express.static can't resolve a
760
+ // directory request without an index. Redirect to /builders/diagrams (no slash)
761
+ // so the diagrams.html page actually renders. The no-slash form already works
762
+ // via the extensions: ['html'] resolution on buildersStatic.
763
+ //
764
+ // Implementation notes:
765
+ // - REGEX route, not a string. With Express's default non-strict routing,
766
+ // the string form '/builders/diagrams/' matches both '/.../diagrams' AND
767
+ // '/.../diagrams/', which would cause a redirect loop. Anchoring with
768
+ // ^…$ + a literal trailing slash gates the match to the with-slash form.
769
+ // - Registered BEFORE the diagramsStatic wrapper below — that wrapper
770
+ // mutates req.url for /builders/diagrams/* paths, which would prevent
771
+ // this exact-path redirect from matching if it ran first.
772
+ app.get(/^\/builders\/diagrams\/$/, (req, res, next) => {
773
+ if (isModuleSurfaceHost(req)) return next();
774
+ res.redirect(301, '/builders/diagrams');
775
+ });
776
+
777
+ // task 1965: the read-only Task Board (/board, task 751) was consolidated into
778
+ // the Work page (/work) — the hall keeps one task-listing surface. 301 old
779
+ // bookmarks + deep-links (the apex /builders/board and the bare subdomain
780
+ // /board form, with or without a .html suffix) to the surviving Work page,
781
+ // preserving the /builders prefix and any query string.
782
+ app.get(/^(?:\/builders)?\/board(?:\.html)?\/?$/, (req, res, next) => {
783
+ if (isModuleSurfaceHost(req)) return next();
784
+ const prefix = req.path.startsWith('/builders') ? '/builders' : '';
785
+ const qi = req.url.indexOf('?');
786
+ const qs = qi >= 0 ? req.url.slice(qi) : '';
787
+ res.redirect(301, `${prefix}/work${qs}`);
788
+ });
789
+
790
+ // Onboarding diagram PNGs (V3.R76 #295) live in docs/onboarding/diagrams as the
791
+ // single source of truth alongside their Mermaid sources. Serve them here so the
792
+ // builders'-hall diagrams page can embed them without duplicating the binaries.
793
+ // Mounted before buildersStatic so /builders/diagrams/*.png resolves to docs/.
794
+ const diagramsStatic = express.static(path.join(ROOT, 'docs/onboarding/diagrams'), {
795
+ setHeaders: (res) => res.setHeader('Cache-Control', 'public, max-age=300'),
796
+ });
797
+ app.use((req, res, next) => {
798
+ if (isModuleSurfaceHost(req)) return next();
799
+ if (req.path.startsWith('/builders/diagrams/')) {
800
+ req.url = req.url.replace(/^\/builders\/diagrams\/?/, '/') || '/';
801
+ return diagramsStatic(req, res, next);
802
+ }
803
+ next();
804
+ });
805
+
806
+ // Citizen's primer (V3.R13-followup #406) — the long-form reading source for
807
+ // the /builders/primer page. The canonical markdown lives at
808
+ // docs/onboarding/primer.md (single source of truth, also linked from the CLI
809
+ // nudges + the repo). Mounted as a single static file so primer.js can fetch
810
+ // and render it client-side without a build step. Same mount pattern as
811
+ // diagramsStatic above.
812
+ app.get('/builders/primer.md', (req, res, next) => {
813
+ if (isModuleSurfaceHost(req)) return next();
814
+ res.set('Cache-Control', 'public, max-age=300');
815
+ res.set('Content-Type', 'text/markdown; charset=utf-8');
816
+ res.sendFile(path.join(ROOT, 'docs/onboarding/primer.md'));
817
+ });
818
+
819
+ // Drachmae & karma reader (task 1472) — the builder-facing "how you get paid"
820
+ // doc. The canonical markdown lives at docs/onboarding/drachmae-and-karma.md
821
+ // (single source of truth, also linked from the primer + the repo); the
822
+ // /builders/drachmae page (drachmae.js + md-reader.js) fetches + renders it
823
+ // client-side. Same static-file mount pattern as primer.md above; public
824
+ // like the /ranks page — economy transparency, no sign-in gate.
825
+ app.get('/builders/drachmae-and-karma.md', (req, res, next) => {
826
+ if (isModuleSurfaceHost(req)) return next();
827
+ res.set('Cache-Control', 'public, max-age=300');
828
+ res.set('Content-Type', 'text/markdown; charset=utf-8');
829
+ res.sendFile(path.join(ROOT, 'docs/onboarding/drachmae-and-karma.md'));
830
+ });
831
+
832
+ // Rendered API docs (task 1918, BONGOS-V1). A self-describing OpenAPI 3.1 site
833
+ // at /docs — cloudbongos.com/docs is the advertised canonical URL, and it works
834
+ // on the apex of every instance (mounted in the SHARED surface, so a self-hosted
835
+ // Cloud Bongos is self-documenting for free). Served on the apex host; the
836
+ // status./builders. module hosts skip it (isModuleSurfaceHost). The renderer
837
+ // (Redoc) + the generated spec are vendored/generated static assets — no CDN, no
838
+ // external fetch (matches the /vendor/* self-hosting convention; CSP-safe).
839
+ // GET /docs → the Redoc host page (branding-injected)
840
+ // GET /docs/redoc.standalone.js → the vendored renderer (long-cached)
841
+ // GET /docs/openapi.json → the generated spec (CORS-open so Postman/Swagger/AI can fetch it)
842
+ app.get('/docs', (req, res, next) => {
843
+ if (isModuleSurfaceHost(req)) return next();
844
+ const html = docsIndexHtml();
845
+ if (!html) return next(); // instance ships no docs page → fall through to 404
846
+ res.set('Cache-Control', 'public, max-age=60');
847
+ res.type('html');
848
+ res.send(html);
849
+ });
850
+ app.get('/docs/redoc.standalone.js', (req, res, next) => {
851
+ if (isModuleSurfaceHost(req)) return next();
852
+ res.set('Cache-Control', 'public, max-age=86400');
853
+ res.type('application/javascript');
854
+ res.sendFile(path.join(ROOT, 'public-docs/redoc.standalone.js'));
855
+ });
856
+ app.get('/docs/openapi.json', (req, res, next) => {
857
+ if (isModuleSurfaceHost(req)) return next();
858
+ res.set('Access-Control-Allow-Origin', '*'); // read-only spec — tools fetch it cross-origin
859
+ res.set('Cache-Control', 'public, max-age=60');
860
+ res.type('application/json');
861
+ res.sendFile(path.join(ROOT, 'docs/api/openapi.json'));
862
+ });
863
+
864
+ app.use((req, res, next) => {
865
+ if (isModuleSurfaceHost(req)) return next();
866
+ if (req.path === '/builders' || req.path.startsWith('/builders/')) {
867
+ // Serve HTML pages from the stamped in-memory copy (asset ?v= = content
868
+ // hash). Gated pages (primer, diagrams) have already passed the sign-in
869
+ // gate above before reaching here. Everything else (js/css/png/json,
870
+ // unknown paths) falls through to the on-disk static handler.
871
+ if (req.method === 'GET' || req.method === 'HEAD') {
872
+ const htmlName = buildersHtmlName(req.path);
873
+ if (htmlName) {
874
+ res.set('Cache-Control', 'public, max-age=60');
875
+ res.type('html');
876
+ return res.send(buildersHtml[htmlName]);
877
+ }
878
+ }
879
+ // Strip the /builders prefix so index.html resolves at the root.
880
+ req.url = req.url.replace(/^\/builders\/?/, '/') || '/';
881
+ return buildersStatic(req, res, next);
882
+ }
883
+ next();
884
+ });
885
+
886
+ return app;
887
+ }
888
+
889
+ module.exports = { mountInternalSurfaces, isStatusHost, isBuildersHost, injectBrandingGlobal, injectBrandingInto, injectModuleHallWidgets, themeMarkup, applyBrandTokens };