@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,755 @@
1
+ const express = require('express');
2
+ const crypto = require('node:crypto');
3
+
4
+ const auth = require('../auth');
5
+ const db = require('../db');
6
+ const { pairStore } = require('../app-pair');
7
+ const { publicOrigin, parseCookie, validateOrRespond } = require('./_helpers');
8
+ const { branding } = require('../../branding');
9
+ const seams = require('../../module-seams');
10
+
11
+ function escapeHtml(s) {
12
+ return String(s ?? '').replace(/[&<>"']/g, (c) => ({
13
+ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
14
+ }[c]));
15
+ }
16
+
17
+ // Render a small standalone HTML page in the builders' hall palette. Used for
18
+ // OAuth error branches so the visitor sees something coherent rather than a
19
+ // text/plain trace. Kept inline (no template engine) so it has no runtime deps.
20
+ // opts (all optional):
21
+ // subtitle — replaces the default header sub-line
22
+ // cta — { href, label } for the primary button (default: retry sign-in)
23
+ // note — replaces the default troubleshooting footnote
24
+ function renderAuthErrorPage(heading, detail, opts = {}) {
25
+ // Every fixed string on this page — the tab-title suffix, epigraph, page
26
+ // heading, sub-line, and retry button — is owned by the instance branding
27
+ // pack (config/branding.json `copy.authError`) so a non-OTB instance renders
28
+ // its OWN words, never OTB lore, on cloudbongos.com and beyond (ADR 0062 §3;
29
+ // task 1439 / idea #351). It is resolved defensively — a branding hiccup
30
+ // falls back to neutral copy rather than crashing the error page, mirroring
31
+ // the worldName pattern below. `authError` is server-only (rendered here), so
32
+ // it is deliberately NOT part of the clientBranding allow-list. Callers may
33
+ // still override the sub-line / cta / note per error branch via `opts`.
34
+ const AUTH_ERROR_FALLBACK = {
35
+ titleSuffix: 'sign-in failed',
36
+ epigraph: '— access —',
37
+ heading: 'Sign-in failed',
38
+ subtitle: "The sign-in didn't complete. Try again, or contact the owner.",
39
+ ctaLabel: 'Try signing in again',
40
+ };
41
+ const copy = (() => {
42
+ try { return { ...AUTH_ERROR_FALLBACK, ...(branding().copy?.authError || {}) }; }
43
+ catch { return AUTH_ERROR_FALLBACK; }
44
+ })();
45
+ const subtitle = opts.subtitle || copy.subtitle;
46
+ const cta = opts.cta || { href: '/api/gds/auth/web/start', label: copy.ctaLabel };
47
+ const note = opts.note || "If this keeps happening, the GitHub OAuth app's authorized callback URL may be misconfigured. See docs/recipes/ops-gotchas.md (GitHub OAuth section).";
48
+ // The instance name in the tab title comes from the branding pack, never a
49
+ // hardcoded host brand — a non-OTB instance sees its own name (ADR 0062 §3).
50
+ const worldName = (() => { try { return branding().identity?.worldName || 'The city'; } catch { return 'The city'; } })();
51
+ return `<!doctype html>
52
+ <html lang="en"><head>
53
+ <meta charset="utf-8">
54
+ <meta name="viewport" content="width=device-width, initial-scale=1">
55
+ <title>${escapeHtml(worldName)} — ${escapeHtml(copy.titleSuffix)}</title>
56
+ <link rel="preconnect" href="https://fonts.googleapis.com">
57
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
58
+ <link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@500;700&family=EB+Garamond:ital,wght@0,400;0,500&display=swap" rel="stylesheet">
59
+ <link rel="stylesheet" href="/builders/style.css">
60
+ </head><body>
61
+ <header class="hall">
62
+ <p class="hall__epigraph">${escapeHtml(copy.epigraph)}</p>
63
+ <h1>${escapeHtml(copy.heading)}</h1>
64
+ <p class="hall__sub">${escapeHtml(subtitle)}</p>
65
+ </header>
66
+ <main>
67
+ <section class="scroll">
68
+ <div class="landing">
69
+ <div class="landing__error">
70
+ <p class="landing__error-h">${escapeHtml(heading)}</p>
71
+ <p>${escapeHtml(detail)}</p>
72
+ </div>
73
+ <p class="landing__cta">
74
+ <a class="landing__btn" href="${escapeHtml(cta.href)}">${escapeHtml(cta.label)}</a>
75
+ </p>
76
+ <p class="landing__note">${escapeHtml(note)}</p>
77
+ </div>
78
+ </section>
79
+ </main>
80
+ </body></html>`;
81
+ }
82
+
83
+ // Cookie names. The web flow writes the new `gds_session` (+ `gds_oauth_state`)
84
+ // names; both flows accept either the new or the legacy `pms_session` /
85
+ // `pms_oauth_state` cookies on read so a builder mid-flow doesn't have to
86
+ // re-auth at the rename rollout.
87
+ const SESSION_COOKIE = 'gds_session';
88
+ const SESSION_COOKIE_LEGACY = 'pms_session';
89
+ const STATE_COOKIE = 'gds_oauth_state';
90
+ const STATE_COOKIE_LEGACY = 'pms_oauth_state';
91
+ const DISCORD_STATE_COOKIE = 'gds_discord_state';
92
+ // Short-lived cookie carrying a validated post-sign-in return URL so the web
93
+ // flow can land a builder back on the subdomain they started from (#726). Set
94
+ // at /auth/web/start, consumed + cleared at the callback. Defaults to /builders.
95
+ const RETURN_COOKIE = 'gds_return';
96
+
97
+ // The apex domain the instance is served under, read from the instance branding
98
+ // pack's cookieDomain (".amazonprimea.com" → "amazonprimea.com",
99
+ // ".cloudbongos.com" → "cloudbongos.com"). The session + OAuth-state cookies are
100
+ // scoped to ".<apex>" so a single sign-in is shared across every subdomain
101
+ // (apex, builders., status.) — the enabler for the dedicated builders subdomain
102
+ // (#726). MUST come from config, not a hardcoded literal: a hardcoded
103
+ // "amazonprimea.com" made every OTHER instance (e.g. cloudbongos.com) set a
104
+ // cookie for the wrong domain, which the browser drops → the OAuth state cookie
105
+ // never persisted → "state mismatch / the gate is closed" (ADR 0062 §3/§5: domains
106
+ // woven into auth, config swaps must not widen cookie scope). Each instance scopes
107
+ // its OWN apex; neither can set the other's cookie. An instance with no
108
+ // cookieDomain (localhost dev, a misconfigured pack) → "" → host-only cookies
109
+ // everywhere (fail-safe, never cross-host). Resolved once at module load (the
110
+ // brand is a per-process singleton).
111
+ function configuredCookieApex() {
112
+ try {
113
+ return String(branding().domains?.cookieDomain || '').replace(/^\./, '').toLowerCase();
114
+ } catch {
115
+ return '';
116
+ }
117
+ }
118
+ const COOKIE_APEX = configuredCookieApex();
119
+
120
+ // Reqest host, lowercased, port-stripped. x-forwarded-host wins behind the proxy.
121
+ function reqHost(req) {
122
+ const raw = req.headers['x-forwarded-host'] || req.headers['host'] || '';
123
+ return String(raw).toLowerCase().split(',')[0].trim().split(':')[0];
124
+ }
125
+
126
+ // The production-facing hosts that share one sign-in. Deliberately enumerated
127
+ // (not "any *.amazonprimea.com") so staging.amazonprimea.com — a SEPARATE
128
+ // process + DB on :3001 — keeps a host-only cookie and never bleeds an
129
+ // apex-scoped token into prod (or vice versa).
130
+ // When COOKIE_APEX is "" (localhost / no configured cookieDomain) the set is
131
+ // EMPTY → every host gets a host-only cookie (never a "." + "" = ".<empty>" leak).
132
+ const APEX_COOKIE_HOSTS = new Set(
133
+ COOKIE_APEX
134
+ ? [COOKIE_APEX, 'www.' + COOKIE_APEX, 'builders.' + COOKIE_APEX, 'status.' + COOKIE_APEX]
135
+ : []
136
+ );
137
+
138
+ // Cookie attributes that vary by host. On the production-facing hosts the
139
+ // session cookie is scoped to the apex so it crosses subdomains (the enabler
140
+ // for builders.amazonprimea.com, #726); everywhere else (localhost, staging)
141
+ // it stays host-only. Returns a spread-able object.
142
+ function sessionCookieDomainOpts(req) {
143
+ if (APEX_COOKIE_HOSTS.has(reqHost(req))) {
144
+ return { domain: '.' + COOKIE_APEX };
145
+ }
146
+ return {};
147
+ }
148
+
149
+ // Validate a caller-supplied post-sign-in return target. Accept ONLY:
150
+ // - a relative path beginning with a single '/' (not '//', no backslash), or
151
+ // - an absolute https URL whose host is the apex or a *.amazonprimea.com sub.
152
+ // Anything else (other origins, javascript:, protocol-relative) → null, so the
153
+ // caller falls back to the safe default. This is an open-redirect guard.
154
+ function safeReturnTo(raw) {
155
+ if (!raw || typeof raw !== 'string' || raw.length > 512) return null;
156
+ // Relative path — must start with exactly one slash and no backslash.
157
+ if (raw.startsWith('/')) {
158
+ if (raw.startsWith('//') || raw.includes('\\')) return null;
159
+ return raw;
160
+ }
161
+ let u;
162
+ try { u = new URL(raw); } catch { return null; }
163
+ if (u.protocol !== 'https:') return null;
164
+ const h = u.hostname.toLowerCase();
165
+ if (h === COOKIE_APEX || h.endsWith('.' + COOKIE_APEX)) return u.toString();
166
+ return null;
167
+ }
168
+
169
+ // The canonical sign-in origin, read from the instance branding pack (oauthOrigin,
170
+ // else publicOrigin) so a non-OTB Cloud Bongos instance pins its OAuth redirects to
171
+ // ITS OWN apex instead of a hardcoded "amazonprimea.com" (ADR 0062 §3/§5; the
172
+ // branding contract). The fixed GDS_PUBLIC_ORIGIN env knob still wins for
173
+ // dev/staging (auth.js's documented FIXED env name, not a <PREFIX>_ one). Resolved
174
+ // once at module load; falls back to '' if branding fails, which leaves the env knob
175
+ // as the sole source rather than baking in a host brand.
176
+ function configuredOAuthOrigin() {
177
+ try {
178
+ const d = branding().domains || {};
179
+ return String(d.oauthOrigin || d.publicOrigin || '');
180
+ } catch {
181
+ return '';
182
+ }
183
+ }
184
+
185
+ // The OAuth redirect_uri must be a FIXED, registered origin — never derived from
186
+ // the request's x-forwarded-host (spoofable; F2 Hacker finding). Configured via
187
+ // env, falling back to the configured canonical public origin, so an injected host
188
+ // cannot rewrite the redirect and Discord's URI registration is not the SOLE backstop.
189
+ const DISCORD_REDIRECT_ORIGIN = process.env.GDS_PUBLIC_ORIGIN || configuredOAuthOrigin();
190
+ const DISCORD_REDIRECT_URI = `${DISCORD_REDIRECT_ORIGIN}/api/gds/auth/discord/callback`;
191
+
192
+ // The GitHub web-OAuth redirect_uri origin (#734). PINNED to the canonical
193
+ // public origin (same env knob as Discord), never the request host — GitHub
194
+ // matches redirect_uri against the app's single registered callback host, so a
195
+ // sign-in started on a builders.* subdomain must still use the apex.
196
+ const WEB_REDIRECT_ORIGIN = process.env.GDS_PUBLIC_ORIGIN || configuredOAuthOrigin();
197
+
198
+ module.exports = function buildAuthRouter() {
199
+ const router = express.Router();
200
+
201
+ // /auth/device — CLI flow
202
+ // rank: public — auth flow itself; identity establishment, not authorization.
203
+ router.post('/auth/device/start', async (_req, res) => {
204
+ if (!auth.authConfigured()) {
205
+ return res.fail('auth_not_configured', { status: 503, message: 'GitHub OAuth App credentials missing on the server. See blockers/blockers.md#github-oauth-app.' });
206
+ }
207
+ try {
208
+ const out = await auth.startDeviceFlow();
209
+ res.json(out);
210
+ } catch (err) {
211
+ console.error('[gds] device/start', err);
212
+ res.fail('device_start_failed', { status: 500, message: 'internal error' });
213
+ }
214
+ });
215
+
216
+ // rank: public — auth flow; the device code itself is the bearer.
217
+ router.post('/auth/device/poll', async (req, res) => {
218
+ if (!auth.authConfigured()) {
219
+ return res.fail('auth_not_configured', 503);
220
+ }
221
+ if (validateOrRespond(req, res, {
222
+ device_code: { required: true, type: 'string', maxLength: 256 },
223
+ })) return;
224
+ const { device_code: deviceCode } = req.body || {};
225
+ if (!deviceCode) return res.fail('device_code_required', 400);
226
+ try {
227
+ const out = await auth.pollDeviceFlow(deviceCode, req.headers['user-agent']);
228
+ res.json(out);
229
+ } catch (err) {
230
+ // Admission gate (SR-19 / task #1000): a first-time GitHub account that
231
+ // hasn't been approved by an Archon can't self-enroll via the CLI device
232
+ // flow either (mirrors the web /auth/web/callback NOT_APPROVED branch).
233
+ // Return a structured 403 with state:'not_approved' so setup.js can print a
234
+ // friendly "ask an Archon to invite you" message instead of a raw error.
235
+ if (err && err.code === 'NOT_APPROVED') {
236
+ return res.status(403).json({ state: 'not_approved', error: 'not_approved' });
237
+ }
238
+ console.error('[gds] device/poll', err);
239
+ res.fail('device_poll_failed', { status: 500, message: 'internal error' });
240
+ }
241
+ });
242
+
243
+ // /auth/web — browser flow (used by the /builders page).
244
+ // rank: public — entry into the GitHub OAuth handshake.
245
+ router.get('/auth/web/start', (req, res) => {
246
+ if (!auth.authConfigured()) {
247
+ return res.status(503).type('text/plain').send('auth not configured');
248
+ }
249
+ const state = crypto.randomBytes(16).toString('hex');
250
+ // redirect_uri is PINNED to the canonical origin (#734) — never derived from
251
+ // the request host. GitHub only knows the apex callback, so a sign-in begun
252
+ // on builders.amazonprimea.com must still hand GitHub the apex redirect_uri
253
+ // or GitHub rejects it. Same posture the Discord flow already uses.
254
+ const redirectUri = `${WEB_REDIRECT_ORIGIN}/api/gds/auth/web/callback`;
255
+ res.cookie(STATE_COOKIE, state, {
256
+ httpOnly: true,
257
+ sameSite: 'lax',
258
+ secure: req.secure || req.headers['x-forwarded-proto'] === 'https',
259
+ maxAge: 10 * 60 * 1000,
260
+ // Apex-scoped (#734) so the state survives the host switch: start may run
261
+ // on the subdomain but the callback always lands on the apex.
262
+ ...sessionCookieDomainOpts(req),
263
+ });
264
+ // Post-sign-in return target (#726/#734): an explicit ?return= wins (set by
265
+ // the page gates); otherwise fall back to the Referer (the page that kicked
266
+ // off sign-in), so the dozens of client 401 redirects land a builder back
267
+ // where they were with no per-call-site change. Both go through the
268
+ // open-redirect allowlist (relative path or *.amazonprimea.com https).
269
+ const returnTo = safeReturnTo(req.query.return) || safeReturnTo(req.headers.referer);
270
+ if (returnTo) {
271
+ res.cookie(RETURN_COOKIE, returnTo, {
272
+ httpOnly: true,
273
+ sameSite: 'lax',
274
+ secure: req.secure || req.headers['x-forwarded-proto'] === 'https',
275
+ maxAge: 10 * 60 * 1000,
276
+ ...sessionCookieDomainOpts(req),
277
+ });
278
+ }
279
+ res.redirect(auth.buildAuthorizeUrl(state, redirectUri));
280
+ });
281
+
282
+ // rank: public — OAuth callback; the state cookie + code are the bearer.
283
+ router.get('/auth/web/callback', async (req, res) => {
284
+ if (!auth.authConfigured()) {
285
+ return res.status(503).type('text/plain').send('auth not configured');
286
+ }
287
+ const expectedState = parseCookie(req, STATE_COOKIE) || parseCookie(req, STATE_COOKIE_LEGACY);
288
+ const { state, code, error } = req.query;
289
+ if (error) {
290
+ return res.status(400).type('text/html').send(renderAuthErrorPage(
291
+ 'GitHub refused the handshake',
292
+ `GitHub returned the error code "${error}". This usually means the OAuth app's authorized callback URL doesn't include this server's /api/gds/auth/web/callback path.`,
293
+ ));
294
+ }
295
+ if (!code || !state || state !== expectedState) {
296
+ return res.status(400).type('text/html').send(renderAuthErrorPage(
297
+ 'State mismatch',
298
+ 'The handshake-cookie did not match the state GitHub returned. This usually means the sign-in took too long (cookie expired after 10 minutes) or the request crossed an origin boundary. Start over and the gate will open.',
299
+ ));
300
+ }
301
+ try {
302
+ // Must match the PINNED redirect_uri used at /auth/web/start (#734) — the
303
+ // GitHub token exchange requires an exact string match.
304
+ const redirectUri = `${WEB_REDIRECT_ORIGIN}/api/gds/auth/web/callback`;
305
+ const { token, expiresAt } = await auth.completeWebFlow({
306
+ code,
307
+ redirectUri,
308
+ userAgent: req.headers['user-agent'],
309
+ });
310
+ res.cookie(SESSION_COOKIE, token, {
311
+ httpOnly: true,
312
+ sameSite: 'lax',
313
+ secure: req.secure || req.headers['x-forwarded-proto'] === 'https',
314
+ path: '/',
315
+ expires: expiresAt,
316
+ // Apex-scoped on the production hosts so one sign-in spans
317
+ // amazonprimea.com + builders. + status. (#726); host-only elsewhere.
318
+ ...sessionCookieDomainOpts(req),
319
+ });
320
+ // The state cookie is apex-scoped now (#734) — clear it with the same
321
+ // domain or the browser keeps it. Clear the host-only form too for any
322
+ // cookie set before this change.
323
+ const domainOpts = sessionCookieDomainOpts(req);
324
+ res.clearCookie(STATE_COOKIE);
325
+ res.clearCookie(STATE_COOKIE_LEGACY);
326
+ if (domainOpts.domain) {
327
+ res.clearCookie(STATE_COOKIE, domainOpts);
328
+ res.clearCookie(STATE_COOKIE_LEGACY, domainOpts);
329
+ }
330
+ // Land back where sign-in began if a validated return target was stashed
331
+ // (e.g. the builders subdomain bounced through here); else the hall.
332
+ const returnTo = safeReturnTo(parseCookie(req, RETURN_COOKIE));
333
+ res.clearCookie(RETURN_COOKIE, domainOpts);
334
+ res.redirect(returnTo || '/builders');
335
+ } catch (err) {
336
+ // Approval gate (2026-05-30): a first-time GitHub account that hasn't been
337
+ // approved by an Archon can't enter. Send them to the hall to request
338
+ // access rather than dead-ending on a generic error.
339
+ if (err && err.code === 'NOT_APPROVED') {
340
+ return res.status(403).type('text/html').send(renderAuthErrorPage(
341
+ 'This account is not approved yet',
342
+ 'Signing in with GitHub is only for builders an Archon has already approved. If you are new, request access at the builders’ hall — an Archon will approve you, then this sign-in will let you in. If you were just approved, the Archon may still need to mark you invited; give it a moment and try again.',
343
+ {
344
+ subtitle: 'Sign-in is for approved builders. New here? Request access first.',
345
+ cta: { href: '/builders', label: 'Go to the hall & request access' },
346
+ note: 'Already approved and still blocked? Ask the Archon to confirm your request was marked “invited.”',
347
+ },
348
+ ));
349
+ }
350
+ console.error('[gds] web/callback', err);
351
+ res.status(500).type('text/html').send(renderAuthErrorPage(
352
+ 'The handshake threw an error',
353
+ `The server tried to exchange the auth code for a session and something went wrong: ${err.message}. Try again; if it persists, the Archon must investigate the server logs.`,
354
+ ));
355
+ }
356
+ });
357
+
358
+ // /auth/discord — account-linking OAuth (Phase 3 F2, ADR 0033 §2). Links a
359
+ // Discord account to the ALREADY-AUTHENTICATED builder (gds_session cookie):
360
+ // the link is self-asserted by the builder, never invented by the bot. Inert
361
+ // (503) until the server has Discord client creds. Mirrors the GitHub web
362
+ // flow's CSRF-state discipline above — a hard requirement (ADR 0033 §2).
363
+ // rank: any-builder — links to your OWN identity; no rank gate.
364
+ router.get('/auth/discord/start', auth.requireBuilder, (req, res) => {
365
+ if (!auth.discordConfigured()) {
366
+ return res.status(503).type('text/plain').send('Discord linking not configured on this server yet.');
367
+ }
368
+ const state = crypto.randomBytes(16).toString('hex');
369
+ const redirectUri = DISCORD_REDIRECT_URI;
370
+ res.cookie(DISCORD_STATE_COOKIE, state, {
371
+ httpOnly: true,
372
+ sameSite: 'lax',
373
+ secure: req.secure || req.headers['x-forwarded-proto'] === 'https',
374
+ maxAge: 10 * 60 * 1000,
375
+ // Apex-scoped (#734 parity) so the state survives the host switch: the
376
+ // link may start on builders.amazonprimea.com but DISCORD_REDIRECT_URI
377
+ // always lands the callback on the apex. A host-only cookie would be
378
+ // dropped on the hop and the state check would fail ("Link handshake
379
+ // expired"). Mirrors the GitHub web-flow state cookie above.
380
+ ...sessionCookieDomainOpts(req),
381
+ });
382
+ res.redirect(auth.buildDiscordAuthorizeUrl(state, redirectUri));
383
+ });
384
+
385
+ // rank: any-builder — completes the link to the caller's own identity.
386
+ router.get('/auth/discord/callback', auth.requireBuilder, async (req, res) => {
387
+ if (!auth.discordConfigured()) {
388
+ return res.status(503).type('text/plain').send('Discord linking not configured on this server yet.');
389
+ }
390
+ const expectedState = parseCookie(req, DISCORD_STATE_COOKIE);
391
+ const { state, code, error } = req.query;
392
+ // The state cookie is apex-scoped now (#734 parity) — clear it with the
393
+ // same domain or the browser keeps it. Clear the host-only form too for any
394
+ // cookie set before this change.
395
+ const stateDomainOpts = sessionCookieDomainOpts(req);
396
+ res.clearCookie(DISCORD_STATE_COOKIE);
397
+ if (stateDomainOpts.domain) res.clearCookie(DISCORD_STATE_COOKIE, stateDomainOpts);
398
+ // CSRF FIRST: validate the state cookie against the state Discord echoes
399
+ // back BEFORE trusting any other param (including Discord's `error`). A
400
+ // forged callback with no valid state — even one carrying `?error=...` — is
401
+ // rejected here, so it can't surface an attacker-chosen message or burn a
402
+ // mid-flow builder's link. Without this a forged callback could otherwise
403
+ // link an attacker's Discord to this builder (ADR 0033 §2).
404
+ if (!state || state !== expectedState) {
405
+ return res.status(400).type('text/html').send(renderAuthErrorPage(
406
+ 'Link handshake expired',
407
+ 'The link-cookie did not match what Discord returned (the sign-in may have taken too long, or crossed an origin). Start over from the hall.',
408
+ { cta: { href: '/builders', label: 'Back to the hall' } },
409
+ ));
410
+ }
411
+ if (error) {
412
+ return res.status(400).type('text/html').send(renderAuthErrorPage(
413
+ 'Discord refused the link',
414
+ `Discord returned "${error}". Try linking again from the builders' hall.`,
415
+ { cta: { href: '/builders', label: 'Back to the hall' } },
416
+ ));
417
+ }
418
+ if (!code) {
419
+ return res.status(400).type('text/html').send(renderAuthErrorPage(
420
+ 'Link handshake incomplete',
421
+ 'Discord did not return an authorization code. Start over from the hall.',
422
+ { cta: { href: '/builders', label: 'Back to the hall' } },
423
+ ));
424
+ }
425
+ try {
426
+ const redirectUri = DISCORD_REDIRECT_URI;
427
+ const accessToken = await auth.exchangeDiscordCode(code, redirectUri);
428
+ const dUser = await auth.fetchDiscordUser(accessToken);
429
+ // Validate the snowflake BEFORE touching the DB (F1 Hacker advisory): a
430
+ // malformed/oversized id never enters discord_links.
431
+ if (!auth.isValidDiscordUserId(dUser && dUser.id)) {
432
+ return res.status(400).type('text/html').send(renderAuthErrorPage(
433
+ 'Could not read your Discord identity',
434
+ 'Discord did not return a valid account id. Try again from the hall.',
435
+ { cta: { href: '/builders', label: 'Back to the hall' } },
436
+ ));
437
+ }
438
+ const result = await db.linkDiscord({ builderId: req.builder.id, discordUserId: dUser.id });
439
+ if (!result.ok && result.error === 'discord_taken') {
440
+ return res.status(409).type('text/html').send(renderAuthErrorPage(
441
+ 'That Discord account is already linked',
442
+ 'This Discord account is linked to a different builder. Unlink it there first, or link a different Discord account.',
443
+ { cta: { href: '/builders', label: 'Back to the hall' } },
444
+ ));
445
+ }
446
+ // F7 auto-join (ADR 0033 — the link-first onboarding gate): now that the
447
+ // builder has self-asserted this Discord account, drop them straight into
448
+ // the guild with their rank role set at add-time, so the role is correct
449
+ // the instant they arrive (no invite link to share or leak). Best-effort:
450
+ // the link must succeed even if the bot is off / lacks CREATE_INSTANT_INVITE
451
+ // / can't reach Discord. We await so a failure can fall through to a
452
+ // graceful message, but never let it throw past here.
453
+ // The guild + rank-role to apply at add-time come from the discord module
454
+ // through the kernel seam (ADR 0083 / BV1.R47 — no core→module import);
455
+ // degrades to { guildId: null } when discord is off, so the join just skips.
456
+ const joinSpec = await seams.resolveOptional(
457
+ 'discord.guildJoinSpec', async () => ({ guildId: null, roleIds: [] }),
458
+ )(req.builder.rank);
459
+ let joined = false;
460
+ if (joinSpec.guildId && auth.discordBotConfigured()) {
461
+ try {
462
+ await auth.addDiscordGuildMember({
463
+ guildId: joinSpec.guildId,
464
+ discordUserId: dUser.id,
465
+ accessToken,
466
+ roleIds: joinSpec.roleIds,
467
+ });
468
+ joined = true;
469
+ } catch (e) {
470
+ console.log('[gds] discord auto-join failed (non-blocking):', e.message);
471
+ }
472
+ }
473
+ // F5 role-sync belt-and-braces (ADR 0033 §4): set/repair the rank-role even
474
+ // if auto-join 204'd an already-present member (whose add-time roles Discord
475
+ // ignores) or the add step was skipped. Emitted to the discord module's
476
+ // listener; fire-and-forget.
477
+ seams.emit('builder.role-sync-needed', { builderId: req.builder.id });
478
+ // #731: the Discord connect card lives on /builders/settings now, so the
479
+ // callback returns there (settings.js surfaces the ?discord= toast).
480
+ res.redirect(joined ? '/builders/settings?discord=joined' : '/builders/settings?discord=linked');
481
+ } catch (err) {
482
+ console.error('[gds] discord/callback', err);
483
+ res.status(500).type('text/html').send(renderAuthErrorPage(
484
+ 'The Discord link threw an error',
485
+ 'Linking failed unexpectedly. Try again; if it persists, the Archon must check the server logs.',
486
+ { cta: { href: '/builders', label: 'Back to the hall' } },
487
+ ));
488
+ }
489
+ });
490
+
491
+ // rank: any-builder — unlink your OWN Discord. JSON (called from the hall UI).
492
+ // F7 follow-up: also kicks the builder from the guild so they can't remain in
493
+ // the server without a linked account. The kick is best-effort — the unlink
494
+ // always succeeds even if the bot lacks Kick Members or the member already left.
495
+ router.post('/auth/discord/unlink', auth.requireBuilder, async (req, res) => {
496
+ try {
497
+ // Read the discord_user_id BEFORE deleting the link — we need it for the kick.
498
+ const link = await db.getDiscordLink(req.builder.id);
499
+ const removed = await db.unlinkDiscord(req.builder.id);
500
+ if (link && auth.discordBotConfigured()) {
501
+ const { guildId } = await seams.resolveOptional(
502
+ 'discord.guildJoinSpec', async () => ({ guildId: null }),
503
+ )(null);
504
+ if (guildId) {
505
+ auth.removeDiscordGuildMember({ guildId, discordUserId: link.discord_user_id })
506
+ .then((r) => console.log(`[gds] discord unlink kick: ${r.removed ? 'removed' : r.reason}`))
507
+ .catch((e) => console.log('[gds] discord unlink kick failed (non-blocking):', e.message));
508
+ }
509
+ }
510
+ res.json({ ok: true, unlinked: removed });
511
+ } catch (err) {
512
+ console.error('[gds] discord/unlink', err);
513
+ res.fail('unlink_failed', 500);
514
+ }
515
+ });
516
+
517
+ // rank: any-builder — own session, no rank gate (a Xenos can log themselves out).
518
+ router.post('/auth/logout', auth.requireBuilder, async (req, res) => {
519
+ const token = (req.headers['authorization'] || '').replace(/^Bearer\s+/, '')
520
+ || parseCookie(req, SESSION_COOKIE)
521
+ || parseCookie(req, SESSION_COOKIE_LEGACY);
522
+ if (token) await db.revokeSession(token);
523
+ // Clear BOTH the host-only form (older sessions) and the apex-scoped form
524
+ // (#726) — a clearCookie only matches the cookie whose domain attribute it
525
+ // names, so sign-out on a production host must target the apex domain too.
526
+ res.clearCookie(SESSION_COOKIE);
527
+ res.clearCookie(SESSION_COOKIE_LEGACY);
528
+ const domainOpts = sessionCookieDomainOpts(req);
529
+ if (domainOpts.domain) {
530
+ res.clearCookie(SESSION_COOKIE, domainOpts);
531
+ res.clearCookie(SESSION_COOKIE_LEGACY, domainOpts);
532
+ }
533
+ res.json({ ok: true });
534
+ });
535
+
536
+ // POST /auth/cli-token/issue — V3.R91 / #401. Issue a fresh CLI bearer for
537
+ // the requesting builder, gated on a WEB COOKIE session (not a bearer). The
538
+ // browser already proves identity via the gds_session cookie; this endpoint
539
+ // mints a separate cli-source session row the builder can paste into
540
+ // ~/.config/otb/gds-session.json via scripts/gds/paste-token.js. Replaces
541
+ // the ~30-60s Device Flow when the CLI bearer has expired.
542
+ //
543
+ // rank: any-builder (own identity only) — no rank gate.
544
+ // auth source restriction: cookie ONLY. A bearer cannot mint a new bearer
545
+ // (a leaked CLI token shouldn't be able to extend its own lifetime; the
546
+ // httpOnly browser cookie is the harder-to-steal credential).
547
+ router.post('/auth/cli-token/issue', auth.requireBuilder, async (req, res) => {
548
+ const cookieToken = parseCookie(req, SESSION_COOKIE) || parseCookie(req, SESSION_COOKIE_LEGACY);
549
+ if (!cookieToken) {
550
+ // Name the instance's own sign-in host (from the branding pack), not a
551
+ // hardcoded "amazonprimea.com" (ADR 0062 §3/§5).
552
+ const signInHost = configuredOAuthOrigin().replace(/^https?:\/\//, '').split('/')[0] || 'the sign-in page';
553
+ return res.fail('cookie_required', { status: 403, message: `This endpoint mints CLI bearers from an authenticated browser session. Sign in at ${signInHost} first; bearer-only requests are rejected so a leaked CLI token cannot mint replacements for itself.` });
554
+ }
555
+ try {
556
+ const token = crypto.randomBytes(32).toString('hex');
557
+ const row = await db.createSession({
558
+ builderId: req.builder.id,
559
+ token,
560
+ // source must be 'cli' | 'web' (003_pms.sql CHECK). The route name in
561
+ // audit_log distinguishes portal-reissue from Device Flow tokens.
562
+ source: 'cli',
563
+ userAgent: req.headers['user-agent'] ?? null,
564
+ // expiresAt: null → createSession applies SESSION_TTL_MS (24h after V3.R90).
565
+ expiresAt: null,
566
+ });
567
+ res.json({
568
+ ok: true,
569
+ token,
570
+ expires_at: row.expires_at,
571
+ builder_login: req.builder.github_login,
572
+ paste_command: `node scripts/gds/paste-token.js ${token}`,
573
+ });
574
+ } catch (err) {
575
+ console.error('[gds] POST /auth/cli-token/issue', err);
576
+ res.fail('issue_failed', { status: 500, message: 'internal error' });
577
+ }
578
+ });
579
+
580
+ // POST /auth/revoke — V3.R38 / #259. Archon-only. Kill a SINGLE session by
581
+ // its primary-key id. Used for surgical lockouts (e.g. a compromised CLI on
582
+ // a known builder's laptop) without disturbing that builder's other sessions.
583
+ // Body: { session_id: <bigint> }. Response: { ok, revoked: 0|1 }.
584
+ // rank: archon — kills an arbitrary session; can lock other builders out.
585
+ router.post('/auth/revoke', auth.requireBuilder, auth.requireRank('archon'), async (req, res) => {
586
+ if (validateOrRespond(req, res, { session_id: {} })) return; // bigint as number or string; validated below
587
+ const sessionId = req.body && req.body.session_id;
588
+ if (sessionId === undefined || sessionId === null) {
589
+ return res.fail('session_id_required', 400);
590
+ }
591
+ // Accept bigint as string or number; validate it parses cleanly.
592
+ const id = Number(sessionId);
593
+ if (!Number.isInteger(id) || id <= 0) {
594
+ return res.fail('bad_session_id', 400);
595
+ }
596
+ try {
597
+ const revoked = await db.revokeSessionById(id);
598
+ res.json({ ok: true, revoked });
599
+ } catch (err) {
600
+ console.error('[gds] POST /auth/revoke', err);
601
+ res.fail('revoke_failed', { status: 500, message: 'internal error' });
602
+ }
603
+ });
604
+
605
+ // POST /auth/revoke-all — V3.R38 / #259. Archon-only. Kill EVERY active
606
+ // session for a given builder. Used at demotion (auto-called from
607
+ // setBuilderRank when newRank moves down the ladder — C7 rank-change-near-
608
+ // instant) and as a manual operator hammer.
609
+ // Body: { builder_id: <bigint> }. Response: { ok, revoked: <count> }.
610
+ // rank: archon — same authority as PATCH /builders/:id/rank.
611
+ router.post('/auth/revoke-all', auth.requireBuilder, auth.requireRank('archon'), async (req, res) => {
612
+ if (validateOrRespond(req, res, { builder_id: {} })) return; // bigint as number or string; validated below
613
+ const builderId = req.body && req.body.builder_id;
614
+ if (builderId === undefined || builderId === null) {
615
+ return res.fail('builder_id_required', 400);
616
+ }
617
+ const id = Number(builderId);
618
+ if (!Number.isInteger(id) || id <= 0) {
619
+ return res.fail('bad_builder_id', 400);
620
+ }
621
+ try {
622
+ const revoked = await db.revokeAllSessionsForBuilder(id);
623
+ res.json({ ok: true, revoked });
624
+ } catch (err) {
625
+ console.error('[gds] POST /auth/revoke-all', err);
626
+ res.fail('revoke_all_failed', { status: 500, message: 'internal error' });
627
+ }
628
+ });
629
+
630
+ // /auth/app-pair — browser-clicks-only sign-in for the Dev Box desktop app
631
+ // (#904, ADR 0045). The app starts a pairing, opens the browser at
632
+ // /builders/pair?code=…, the signed-in builder clicks ONE Approve button,
633
+ // and the app collects a freshly-minted source='cli' session over its own
634
+ // secret poll channel. Design + threat model in src/bongos/app-pair.js.
635
+ //
636
+ // Route order note: the two PUBLIC routes here sit so no requireBuilder/
637
+ // requireRank from a NEIGHBORING declaration falls inside route-rank-check's
638
+ // 20-line look-ahead — the deterministic auditor must classify them from
639
+ // their own `rank:` annotations, not a false middleware hit.
640
+
641
+ // rank: public — starts a pairing; returns opaque codes only, no identity.
642
+ router.post('/auth/app-pair/start', (req, res) => {
643
+ if (validateOrRespond(req, res, {
644
+ device_name: { type: 'string', maxLength: 80 },
645
+ platform: { type: 'string', maxLength: 20 },
646
+ })) return;
647
+ const body = req.body || {};
648
+ const out = pairStore.start({ deviceName: body.device_name, platform: body.platform });
649
+ if (out.error === 'busy') {
650
+ return res.fail('pairing_busy', { status: 429, message: 'Too many pairings in flight. Try again in a few minutes.' });
651
+ }
652
+ // The approve-page URL is PINNED to the canonical public origin (same
653
+ // posture as the OAuth redirect_uris above) — never derived from the
654
+ // request host, so a spoofed Host header can't point the app's browser
655
+ // hand-off at a look-alike approve page.
656
+ const verifyPath = `/builders/pair?code=${out.pairCode}`;
657
+ res.status(201).json({
658
+ pair_code: out.pairCode,
659
+ poll_token: out.pollToken,
660
+ verify_path: verifyPath,
661
+ verify_url: `${WEB_REDIRECT_ORIGIN}${verifyPath}`,
662
+ expires_in: out.expiresIn,
663
+ interval: out.interval,
664
+ });
665
+ });
666
+
667
+ // rank: any-builder — read-only: what the approve page shows BEFORE the
668
+ // click (device name/platform for the given code). Unknown and expired are
669
+ // indistinguishable (404) so the code space can't be probed for liveness.
670
+ router.get('/auth/app-pair/info', auth.requireBuilder, (req, res) => {
671
+ const info = pairStore.info(req.query.code);
672
+ if (!info) return res.fail('unknown_or_expired', 404);
673
+ res.json({
674
+ pair_code: info.pairCode,
675
+ device_name: info.deviceName,
676
+ platform: info.platform,
677
+ status: info.status,
678
+ });
679
+ });
680
+
681
+ // rank: any-builder (own identity only) — approving signs the app in AS THE
682
+ // APPROVER. Cookie-gated exactly like /auth/cli-token/issue above: a leaked
683
+ // CLI bearer must not be able to approve pairings and mint itself siblings;
684
+ // only the httpOnly browser cookie — the click in the hall — can.
685
+ router.post('/auth/app-pair/approve', auth.requireBuilder, async (req, res) => {
686
+ const cookieToken = parseCookie(req, SESSION_COOKIE) || parseCookie(req, SESSION_COOKIE_LEGACY);
687
+ if (!cookieToken) {
688
+ return res.fail('cookie_required', { status: 403, message: 'Pairings are approved from an authenticated browser session in the builders’ hall, not from a bearer token.' });
689
+ }
690
+ if (validateOrRespond(req, res, {
691
+ pair_code: { required: true, type: 'string', maxLength: 32 },
692
+ })) return;
693
+ try {
694
+ const token = crypto.randomBytes(32).toString('hex');
695
+ const row = await db.createSession({
696
+ builderId: req.builder.id,
697
+ token,
698
+ source: 'cli', // 003_pms.sql CHECK; the app is a CLI-class bearer client
699
+ userAgent: `devbox-app pair (${req.headers['user-agent'] ?? 'unknown'})`,
700
+ expiresAt: null, // createSession applies the 24h refresh-on-use TTL
701
+ });
702
+ const out = pairStore.approve((req.body || {}).pair_code, {
703
+ token,
704
+ expires_at: row.expires_at,
705
+ builder: {
706
+ id: req.builder.id,
707
+ github_login: req.builder.github_login,
708
+ display_name: req.builder.display_name,
709
+ rank: req.builder.rank,
710
+ },
711
+ });
712
+ if (!out.ok) {
713
+ // The pairing was gone or already approved — revoke the session we
714
+ // just minted so an orphaned token never outlives this branch.
715
+ await db.revokeSession(token);
716
+ return out.error === 'already_approved'
717
+ ? res.fail('already_approved', 409)
718
+ : res.fail('unknown_or_expired', 404);
719
+ }
720
+ res.json({ ok: true });
721
+ } catch (err) {
722
+ console.error('[gds] POST /auth/app-pair/approve', err);
723
+ res.fail('approve_failed', { status: 500, message: 'internal error' });
724
+ }
725
+ });
726
+
727
+ // rank: public — poll with the secret poll_token; the token IS the bearer
728
+ // (mirrors /auth/device/poll, where the device_code is the bearer). Kept
729
+ // LAST in this router so route-rank-check's look-ahead window stays clear
730
+ // of any neighboring gated declaration (see the block comment above).
731
+ router.post('/auth/app-pair/poll', (req, res) => {
732
+ if (validateOrRespond(req, res, {
733
+ poll_token: { required: true, type: 'string', maxLength: 128 },
734
+ })) return;
735
+ const out = pairStore.poll((req.body || {}).poll_token);
736
+ if (out.status === 'approved') {
737
+ return res.json({ status: 'approved', ...out.grant });
738
+ }
739
+ res.json(out.status === 'pending'
740
+ ? { status: 'pending', interval: out.interval }
741
+ : { status: 'expired' });
742
+ });
743
+
744
+ return router;
745
+ };
746
+
747
+ // Exposed for unit tests (#726). These are pure host/URL helpers — the
748
+ // open-redirect guard on the post-sign-in return target and the cross-subdomain
749
+ // cookie-domain decision — worth locking down independently of a running server.
750
+ module.exports.safeReturnTo = safeReturnTo;
751
+ module.exports.sessionCookieDomainOpts = sessionCookieDomainOpts;
752
+ module.exports.reqHost = reqHost;
753
+ // The auth-error page renderer (task 1439): pure over the branding pack, so a
754
+ // test can prove every visible string is brand-owned (no OTB lore baked in).
755
+ module.exports.renderAuthErrorPage = renderAuthErrorPage;