@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,916 @@
1
+ // GDS builder-memory sync — HTTP surface over the #273/R54 storage helpers.
2
+ //
3
+ // Implements the push half of ADR 0024 (cloneable repo + local-first memory
4
+ // with server-canonical sync): /builder-ship enumerates the builder's local
5
+ // Claude Code memory dir and POSTs each file here; this route stores them via
6
+ // src/bongos/memory.js (storeMemoryFile), last-write-wins.
7
+ //
8
+ // Scope of THIS task (#274 / V3.R55): the POST /memory/sync endpoint + the
9
+ // ship.js wiring (in scripts/gds/ship.js). The READ side (GET /memory/files,
10
+ // GET /memory/file) + the isolation-hardening + Archon forensic read land in
11
+ // #276 (this task); the pull-on-start wiring lands in #275. Those build ON
12
+ // this.
13
+ //
14
+ // ISOLATION (ADR 0024 §4, same posture as ADR 0016) — the trust boundary of
15
+ // this entire module:
16
+ // * Every SELF endpoint (the common path: POST /memory/sync, GET
17
+ // /memory/files, GET /memory/file) takes its owner ALWAYS from
18
+ // req.builder.id — the authenticated session — NEVER from req.body,
19
+ // req.query, or req.params. There is deliberately no `builder_id` /
20
+ // `owner` input field on any self endpoint. A builder physically cannot
21
+ // read OR write another builder's keyspace through these routes: there is
22
+ // no parameter to even name another builder.
23
+ // * The ONLY endpoints that accept an owner id from input are the two
24
+ // Archon-only forensic reads (GET /memory/builders/:builderId/files and
25
+ // .../file). They are READ-ONLY, gated by requireRank('archon'), and EVERY
26
+ // call writes an audit_log row recording {archon id, target builder id,
27
+ // path|'list', action}. No write path ever accepts a cross-builder owner.
28
+ // This mirrors ADR 0016: enforce server-side, trust nothing a builder can edit
29
+ // locally. The grep-test for a reviewer is simple — search this file for
30
+ // `req.body`, `req.query.builder`, `req.params.builderId`; the only owner
31
+ // derived from input lives under the audited Archon path.
32
+ //
33
+ // RENDER / STORED-XSS (#575, audit 2026-06-19) — memory content is stored RAW
34
+ // and is NEVER emitted as HTML. Every endpoint here returns it as JSON
35
+ // (res.json — content as a string field), and the only consumers are the CLI /
36
+ // dev-box memory sync (it writes the strings to the local filesystem) and the
37
+ // Archon-only forensic reads (also JSON). An audit of every web surface
38
+ // (modules/hall-ui/public/, modules/status-ui/public/) found NO code path that interpolates
39
+ // memory content into the DOM: there is no markdown→HTML renderer in the client
40
+ // at all, and the one place that does render externally-authored markdown — the
41
+ // Archon "Watch" security-doc reader (modules/hall-ui/public/watch.js) — escapes via
42
+ // OTB.escapeHtml + a <pre>. So the stored-XSS vector that the threat model lists
43
+ // for "memory content" (docs/security/threat-model.md §5 external input) is NOT
44
+ // realized today; this is the "record why low-priority" outcome of #575.
45
+ // FORWARD RULE (load-bearing): if a memory-viewer UI is ever added to the hall,
46
+ // it MUST escape the content at the render boundary (OTB.escapeHtml, like
47
+ // watch.js) — memory is builder-controlled free text, never trusted markup.
48
+ //
49
+ // Body limit: the global GDS json parser is capped at 64 KB (routes.js), but a
50
+ // single memory file may be up to 1 MB (memory.js MAX_FILE_BYTES) and a sync
51
+ // batches several files. So this router mounts its OWN express.json with a
52
+ // larger limit, scoped to the /memory/sync path only — it does not relax the
53
+ // 64 KB cap that every other GDS write still runs under. The read endpoints
54
+ // take no body and rely on the global parser.
55
+
56
+ const express = require('express');
57
+ // Carved into modules/memory/ (BV1.R72 / ADR 0091 §2): reach core ONLY through
58
+ // the published doorway. Auth gates + the kernel db helpers this route uses
59
+ // (getBuilderById, insertAuditLog) come off `api`; the memory lib is an
60
+ // intra-module require.
61
+ const api = require('../../../src/module-api');
62
+ const auth = api;
63
+ const memory = require('../memory');
64
+ const db = api;
65
+
66
+ // Per-builder rate limit for POST /memory/sync, layered ON TOP of the global
67
+ // per-IP / per-session limiter (middleware/rate-limit.js, applied to every
68
+ // write). This endpoint is special: a single request can carry up to 50 MB
69
+ // across 2000 files, so even one request/minute is a lot of work — the global
70
+ // 30-writes/min cap is the wrong shape here. We add a tighter sliding-window
71
+ // cap keyed on the AUTHENTICATED builder id (never on input), reusing the same
72
+ // in-memory sliding-window technique as the global limiter rather than
73
+ // inventing a new system. A sync runs once per /builder-ship, so a few per
74
+ // minute is generous headroom; the cap exists to stop a runaway loop from
75
+ // hammering large bodies.
76
+ const SYNC_RATE_WINDOW_MS = 60 * 1000;
77
+ const SYNC_RATE_LIMIT = 6; // syncs per builder per minute
78
+ const syncBuckets = new Map(); // builderId -> timestamp[]
79
+
80
+ const syncSweepTimer = setInterval(() => {
81
+ const cutoff = Date.now() - SYNC_RATE_WINDOW_MS;
82
+ for (const [k, arr] of syncBuckets) {
83
+ while (arr.length && arr[0] < cutoff) arr.shift();
84
+ if (arr.length === 0) syncBuckets.delete(k);
85
+ }
86
+ }, 5 * 60 * 1000);
87
+ if (typeof syncSweepTimer.unref === 'function') syncSweepTimer.unref();
88
+
89
+ // Express middleware: enforce the per-builder sync cap. Runs AFTER
90
+ // auth.requireBuilder so req.builder.id is populated.
91
+ function syncRateLimit(req, res, next) {
92
+ const now = Date.now();
93
+ const key = String(req.builder.id);
94
+ let bucket = syncBuckets.get(key);
95
+ if (!bucket) { bucket = []; syncBuckets.set(key, bucket); }
96
+ const cutoff = now - SYNC_RATE_WINDOW_MS;
97
+ while (bucket.length && bucket[0] < cutoff) bucket.shift();
98
+ if (bucket.length >= SYNC_RATE_LIMIT) {
99
+ const retryAfter = Math.max(1, Math.ceil((bucket[0] + SYNC_RATE_WINDOW_MS - now) / 1000));
100
+ res.set('Retry-After', String(retryAfter));
101
+ res.set('X-RateLimit-Scope', 'per-builder-memory-sync');
102
+ res.set('X-RateLimit-Limit', String(SYNC_RATE_LIMIT));
103
+ res.set('X-RateLimit-Window-Seconds', String(SYNC_RATE_WINDOW_MS / 1000));
104
+ return res.fail('rate_limited', 429, { scope: 'per-builder-memory-sync', retry_after_seconds: retryAfter });
105
+ }
106
+ bucket.push(now);
107
+ return next();
108
+ }
109
+
110
+ // Per-builder rate limit for the READ endpoints (GET /memory/files,
111
+ // GET /memory/file, and the two Archon forensic reads). The global
112
+ // rate-limit middleware (middleware/rate-limit.js) only caps WRITE methods —
113
+ // GET requests bypass it entirely — so without this a pull loop (or a forensic
114
+ // sweep) could hammer reads uncapped. Reads are cheaper than a sync, so the cap
115
+ // is more generous, but it still exists: keyed on the AUTHENTICATED builder id
116
+ // (never on input), same sliding-window technique as the sync limiter and the
117
+ // global limiter. We reuse one bucket map for every read on this router.
118
+ const READ_RATE_WINDOW_MS = 60 * 1000;
119
+ const READ_RATE_LIMIT = 120; // memory reads per builder per minute
120
+ const readBuckets = new Map(); // builderId -> timestamp[]
121
+
122
+ const readSweepTimer = setInterval(() => {
123
+ const cutoff = Date.now() - READ_RATE_WINDOW_MS;
124
+ for (const [k, arr] of readBuckets) {
125
+ while (arr.length && arr[0] < cutoff) arr.shift();
126
+ if (arr.length === 0) readBuckets.delete(k);
127
+ }
128
+ }, 5 * 60 * 1000);
129
+ if (typeof readSweepTimer.unref === 'function') readSweepTimer.unref();
130
+
131
+ // Express middleware: enforce the per-builder read cap. Runs AFTER
132
+ // auth.requireBuilder so req.builder.id is populated. Keyed on the
133
+ // authenticated id only — never on any path/query input.
134
+ function readRateLimit(req, res, next) {
135
+ const now = Date.now();
136
+ const key = String(req.builder.id);
137
+ let bucket = readBuckets.get(key);
138
+ if (!bucket) { bucket = []; readBuckets.set(key, bucket); }
139
+ const cutoff = now - READ_RATE_WINDOW_MS;
140
+ while (bucket.length && bucket[0] < cutoff) bucket.shift();
141
+ if (bucket.length >= READ_RATE_LIMIT) {
142
+ const retryAfter = Math.max(1, Math.ceil((bucket[0] + READ_RATE_WINDOW_MS - now) / 1000));
143
+ res.set('Retry-After', String(retryAfter));
144
+ res.set('X-RateLimit-Scope', 'per-builder-memory-read');
145
+ res.set('X-RateLimit-Limit', String(READ_RATE_LIMIT));
146
+ res.set('X-RateLimit-Window-Seconds', String(READ_RATE_WINDOW_MS / 1000));
147
+ return res.fail('rate_limited', 429, { scope: 'per-builder-memory-read', retry_after_seconds: retryAfter });
148
+ }
149
+ bucket.push(now);
150
+ return next();
151
+ }
152
+
153
+ // assertSelf — the explicit, testable statement of the isolation invariant for
154
+ // the SELF endpoints. It returns the ONLY id a self endpoint may ever use as
155
+ // the memory owner: req.builder.id (the authenticated session). It throws if a
156
+ // caller-supplied owner is present and DOESN'T match the session — defense in
157
+ // depth, since the self endpoints never read such a field in the first place,
158
+ // but it makes the invariant impossible to violate by a later careless edit
159
+ // (e.g. someone wiring a caller-supplied owner-id query param into a handler).
160
+ // The invariant: a self endpoint's owner is ALWAYS the authenticated session
161
+ // (req.builder.id). The ONLY owner-from-input path in this module is the Archon
162
+ // forensic route, which is rank-gated and audited. Reading these helpers and
163
+ // their call sites establishes that a self endpoint cannot return another
164
+ // builder's data, full stop.
165
+ function assertSelf(req, claimedOwnerId) {
166
+ const selfId = req.builder && req.builder.id;
167
+ if (selfId === undefined || selfId === null) {
168
+ // Should be unreachable behind requireBuilder, but fail closed.
169
+ throw { code: 'NOT_AUTHENTICATED' };
170
+ }
171
+ if (
172
+ claimedOwnerId !== undefined &&
173
+ claimedOwnerId !== null &&
174
+ String(claimedOwnerId) !== String(selfId)
175
+ ) {
176
+ throw { code: 'CROSS_BUILDER_FORBIDDEN', self: selfId, claimed: claimedOwnerId };
177
+ }
178
+ return selfId;
179
+ }
180
+
181
+ // recordForensicRead — write an audit_log row for an Archon's cross-builder
182
+ // memory read. The global audit middleware only instruments mutating methods
183
+ // (POST/PATCH/PUT/DELETE), and these are GETs, so it does NOT fire for them —
184
+ // we record explicitly here so every forensic look is logged (the done-when
185
+ // requirement). The audit_log.method column is CHECK-constrained to a write
186
+ // verb, so we record the action as 'POST' (it IS a logged, privileged action)
187
+ // and carry the true semantics in route + the redacted body.
188
+ //
189
+ // FAIL-CLOSED (V3.R57): unlike the global audit middleware (which
190
+ // fires after the response and may swallow), this MUST be awaited BEFORE the
191
+ // Archon is shown any of the target builder's data, and a failed insert MUST
192
+ // surface to the caller as a rejected promise. The invariant is that no
193
+ // forensic cross-builder read is ever served without a durable audit row — so
194
+ // the caller awaits this, and on rejection returns 500 and withholds the data.
195
+ // We do NOT catch/swallow here: the catch belongs to the route, which turns a
196
+ // rejection into a 500 (and logs it) rather than leaking the target's content.
197
+ //
198
+ // `responseStatus` is the ACTUAL outcome of the look (200 when a file/list was
199
+ // returned, 404 when the target file was absent) so the forensic trail is
200
+ // accurate rather than always claiming 200.
201
+ function recordForensicRead(req, { route, targetBuilderId, target, responseStatus = 200, action = 'archon_forensic_memory_read' }, insertFn) {
202
+ const insert = typeof insertFn === 'function' ? insertFn : db.insertAuditLog;
203
+ // Return the insert promise directly (awaited by the caller). Any rejection
204
+ // propagates — the route's try/catch fails the read closed.
205
+ return Promise.resolve(
206
+ insert({
207
+ builderId: req.builder.id,
208
+ route,
209
+ method: 'POST', // CHECK constraint forbids 'GET'; semantics live in the body
210
+ requestBodyRedacted: {
211
+ // 'archon_forensic_memory_read' (Archon rank-gated) or 'bfg_memory_read'
212
+ // (BFG-principal-gated, 6B.1) — auditors query by this action.
213
+ action,
214
+ http_method: 'GET',
215
+ target_builder_id: targetBuilderId,
216
+ target, // a path string, or 'list'
217
+ },
218
+ responseStatus,
219
+ ip: req.ip || (req.socket && req.socket.remoteAddress) || null,
220
+ userAgent: (req.headers && req.headers['user-agent']) || null,
221
+ })
222
+ );
223
+ }
224
+
225
+ // Generous batch cap: MAX_BUILDER_BYTES (50 MB) + framing headroom. A push
226
+ // that would exceed the per-builder quota is still rejected per-file with a
227
+ // clean 413 below; this limit only stops a pathological request body from
228
+ // being buffered into memory. Files-per-request is capped separately.
229
+ const SYNC_BODY_LIMIT = '64mb';
230
+ const MAX_FILES_PER_SYNC = memory.MAX_BUILDER_FILES; // 2000 — same as the per-builder file cap
231
+
232
+ // Kill-switch (ADR 0026 §6C): the cross-builder WRITE path is DISABLED unless
233
+ // BFG_WRITE_ENABLED === 'true' in the environment. Fail-safe default — this is a
234
+ // deliberate, bounded puncture of the trust boundary (ADR 0016), so the capability
235
+ // stays OFF until an operator turns it on (set in the droplet EnvironmentFile,
236
+ // then restart). Unsetting it + restart instantly closes the path again. Checked
237
+ // per-request so the running process reflects the env at request time. Pure +
238
+ // exported so tests can drive it via process.env without standing up the server.
239
+ function bfgWriteEnabled() {
240
+ return process.env.BFG_WRITE_ENABLED === 'true';
241
+ }
242
+
243
+ module.exports = function buildMemoryRouter() {
244
+ const router = express.Router();
245
+
246
+ // Larger body parser, scoped to this path only. Mounted before the handler
247
+ // so the route sees the parsed array. routes.js explicitly SKIPS the global
248
+ // 64 KB parser for the /memory/sync path (its mounter checks req.path and
249
+ // calls next() without parsing), so this router's own express.json is the
250
+ // ONLY body parser that runs here — making SYNC_BODY_LIMIT the effective cap.
251
+ const syncJson = express.json({ limit: SYNC_BODY_LIMIT });
252
+
253
+ // POST /memory/sync — push a builder's local memory files to the server.
254
+ //
255
+ // Auth: any authenticated builder (requireBuilder). A builder can only ever
256
+ // write their OWN memory — the owner is req.builder.id, never from input.
257
+ //
258
+ // Request body (two accepted shapes):
259
+ // { files: [ { path, content, mtime? }, ... ] } (preferred)
260
+ // [ { path, content, mtime? }, ... ] (bare array, also ok)
261
+ // where:
262
+ // path — string, relative path under the memory dir (e.g. 'MEMORY.md').
263
+ // Validated by storeMemoryFile (no leading '/', no '..', no NUL).
264
+ // content — string, the file body (UTF-8).
265
+ // mtime — optional ISO-8601 string or epoch ms; the source file's mtime.
266
+ // Stored for the pull side's newer-than check. Server clock is the
267
+ // conflict authority (last-write-wins), so this is advisory.
268
+ //
269
+ // Response 200 (always 200 when the request itself was well-formed; per-file
270
+ // outcomes are in the body so one bad file doesn't sink the batch):
271
+ // {
272
+ // stored: <count of files upserted>,
273
+ // skipped: <count of files skipped — e.g. non-string content>,
274
+ // errors: [ { path, code, message, hint? }, ... ],
275
+ // results: [ { path, stored: true, sha256, byte_len, updated_at } | { path, error: {...} } ]
276
+ // }
277
+ // A quota/oversize failure on a file is reported in errors[] with code
278
+ // FILE_TOO_LARGE / MEMORY_QUOTA_EXCEEDED and an HTTP 413 IF ANY file tripped a
279
+ // quota (so the client knows to prune), otherwise HTTP 200. Malformed paths
280
+ // (BAD_PATH) are per-file errors and do NOT force a non-200 — they're a client
281
+ // bug, not a quota signal.
282
+ router.post('/memory/sync', auth.requireBuilder, syncRateLimit, syncJson, async (req, res) => {
283
+ const builderId = req.builder.id;
284
+
285
+ // strict:false — /memory/sync accepts a bare ARRAY or { files: [...] } (dual
286
+ // shape), and the block below does the authoritative shape/size validation;
287
+ // this declares `files` for the spec + type-checks the object form only.
288
+ if (api.validateOrRespond(req, res, { files: { type: 'array' } }, { strict: false })) return;
289
+
290
+ // Normalize the two accepted body shapes into an array of file entries.
291
+ const body = req.body;
292
+ let files;
293
+ if (Array.isArray(body)) {
294
+ files = body;
295
+ } else if (body && Array.isArray(body.files)) {
296
+ files = body.files;
297
+ } else {
298
+ return res.fail('bad_request', { status: 400, message: 'body must be an array of {path, content, mtime?} or { files: [...] }' });
299
+ }
300
+
301
+ if (files.length > MAX_FILES_PER_SYNC) {
302
+ return res.fail('too_many_files', { status: 413, message: `at most ${MAX_FILES_PER_SYNC} files per sync`, details: { count: files.length, max: MAX_FILES_PER_SYNC } });
303
+ }
304
+
305
+ const results = [];
306
+ const errors = [];
307
+ let stored = 0;
308
+ let skipped = 0;
309
+ let quotaTripped = false;
310
+
311
+ // First pass: filter out the files that fail input-shape validation
312
+ // (BAD_PATH / BAD_CONTENT) — those are the caller's bug and never reach the
313
+ // DB. The rest go to the batch helper, which does all upserts + the
314
+ // per-builder quota check inside ONE transaction (was N transactions —
315
+ // one per file — before this fix).
316
+ const toStore = []; // { path, content, mtime } entries for the batch
317
+ const placeholder = []; // results[] slots to fill from the batch, in order
318
+ for (const f of files) {
319
+ const path = f && typeof f.path === 'string' ? f.path : null;
320
+ if (!path) {
321
+ skipped += 1;
322
+ const err = { path: path ?? '<missing>', code: 'BAD_PATH', message: 'path must be a non-empty string' };
323
+ errors.push(err);
324
+ results.push({ path: err.path, error: { code: err.code, message: 'internal error' } });
325
+ continue;
326
+ }
327
+ if (typeof f.content !== 'string') {
328
+ skipped += 1;
329
+ const err = { path, code: 'BAD_CONTENT', message: 'content must be a string' };
330
+ errors.push(err);
331
+ results.push({ path, error: { code: err.code, message: 'internal error' } });
332
+ continue;
333
+ }
334
+ // Reserved-namespace guard (ADR 0026 §6C): a builder cannot SYNC into the
335
+ // BFG's keyspace. `bfg/` is exclusively BFG-written, so the BFG never
336
+ // collides with — nor overwrites — a builder's own file. The builder can
337
+ // still READ and DELETE files under bfg/ (their own keyspace); they just
338
+ // can't create/overwrite them via sync.
339
+ if (memory.isReservedNamespacePath(path)) {
340
+ skipped += 1;
341
+ const err = { path, code: 'RESERVED_NAMESPACE', message: `'${memory.BFG_NAMESPACE_PREFIX}' is reserved for BFG-authored notes and cannot be synced into` };
342
+ errors.push(err);
343
+ results.push({ path, error: { code: err.code, message: 'internal error' } });
344
+ continue;
345
+ }
346
+ // Reserve this file's slot in results[] now so order is preserved.
347
+ const slot = results.length;
348
+ results.push(null);
349
+ placeholder.push(slot);
350
+ toStore.push({ path, content: f.content, mtime: f.mtime ?? null });
351
+ }
352
+
353
+ // Single transaction for every well-formed file (N+1 fix).
354
+ let batch = [];
355
+ try {
356
+ batch = await memory.storeMemoryFilesBatch(builderId, toStore);
357
+ } catch (e) {
358
+ // A throw here means the transaction itself failed (BEGIN/COMMIT/connect),
359
+ // not a per-file rejection — those come back inside the results array.
360
+ console.error('[gds] POST /memory/sync batch transaction error', { builderId, err: e });
361
+ return res.fail('store_failed', { status: 500, message: 'memory sync failed; nothing was stored' });
362
+ }
363
+
364
+ // Fold the batch's per-file outcomes back into the response, preserving the
365
+ // exact shape (and the exact error mapping) the old per-file loop produced.
366
+ for (let i = 0; i < batch.length; i++) {
367
+ const slot = placeholder[i];
368
+ const r = batch[i];
369
+ const path = toStore[i].path;
370
+ if (r && r.ok) {
371
+ const row = r.row;
372
+ stored += 1;
373
+ results[slot] = {
374
+ path: row.path,
375
+ stored: true,
376
+ sha256: row.sha256,
377
+ byte_len: Number(row.byte_len),
378
+ updated_at: row.updated_at,
379
+ };
380
+ continue;
381
+ }
382
+ // Per-file rejection — same code/hint mapping as before.
383
+ const e = r && r.error ? r.error : null;
384
+ const code = e && e.code ? e.code : 'STORE_FAILED';
385
+ let message = (e && e.message) || 'store failed';
386
+ let hint;
387
+ if (code === 'FILE_TOO_LARGE') {
388
+ quotaTripped = true;
389
+ message = `file exceeds the ${memory.MAX_FILE_BYTES}-byte per-file limit`;
390
+ hint = 'Prune or split this file before syncing; the server stores files individually.';
391
+ } else if (code === 'MEMORY_QUOTA_EXCEEDED') {
392
+ quotaTripped = true;
393
+ message =
394
+ e.kind === 'file_count'
395
+ ? `would exceed the ${memory.MAX_BUILDER_FILES}-file per-builder limit`
396
+ : `would exceed the ${memory.MAX_BUILDER_BYTES}-byte per-builder limit`;
397
+ hint = 'Prune old memory files (the limit is per-builder, summed across all files).';
398
+ } else if (code !== 'BAD_PATH' && code !== 'STORE_FAILED') {
399
+ // Unknown helper error code — log for forensics, still report per-file.
400
+ console.error('[gds] POST /memory/sync store error', { builderId, path, code, err: e });
401
+ } else if (code === 'STORE_FAILED') {
402
+ console.error('[gds] POST /memory/sync unexpected error', { builderId, path, err: e });
403
+ }
404
+ const err = { path, code, message, ...(hint ? { hint } : {}) };
405
+ errors.push(err);
406
+ results[slot] = { path, error: { code, message, ...(hint ? { hint } : {}) } };
407
+ }
408
+
409
+ const payload = { stored, skipped, errors, results };
410
+ // 413 only when a quota/size limit was actually hit, so the client knows to
411
+ // prune. Malformed-path / bad-content errors stay on a 200 — the batch was
412
+ // accepted, those individual files are just the caller's bug.
413
+ res.status(quotaTripped ? 413 : 200).json(payload);
414
+ });
415
+
416
+ // -----------------------------------------------------------------------
417
+ // READ side (#276 / V3.R57) — strictly self-scoped. The pull side (#275)
418
+ // and a builder inspecting their own memory call these. The owner is ALWAYS
419
+ // req.builder.id; there is no parameter to name another builder, so these
420
+ // are inherently isolated — assertSelf() states that invariant explicitly.
421
+ // -----------------------------------------------------------------------
422
+
423
+ // GET /memory/files — list the AUTHENTICATED builder's memory files
424
+ // (metadata only, no content). Single query (listMemoryFiles is one SELECT —
425
+ // no N+1). Returns newest-changed first.
426
+ //
427
+ // Auth: any authenticated builder (requireBuilder). Self-scoped: owner is
428
+ // req.builder.id. NO owner param is accepted.
429
+ //
430
+ // Response 200:
431
+ // { builder_id, count, files: [ { path, sha256, byte_len, mtime, updated_at, created_at }, ... ] }
432
+ router.get('/memory/files', auth.requireBuilder, readRateLimit, async (req, res) => {
433
+ let builderId;
434
+ try {
435
+ builderId = assertSelf(req); // owner is the session, never input
436
+ } catch (_e) {
437
+ return res.fail('unauthenticated', 401);
438
+ }
439
+ try {
440
+ const rows = await memory.listMemoryFiles(builderId);
441
+ res.set('Cache-Control', 'no-store');
442
+ return res.json({
443
+ builder_id: builderId,
444
+ count: rows.length,
445
+ files: rows.map((r) => ({
446
+ path: r.path,
447
+ sha256: r.sha256,
448
+ byte_len: Number(r.byte_len),
449
+ mtime: r.mtime,
450
+ updated_at: r.updated_at,
451
+ created_at: r.created_at,
452
+ })),
453
+ });
454
+ } catch (err) {
455
+ console.error('[gds] GET /memory/files', { builderId, err });
456
+ return res.fail('list_failed', { status: 500, message: 'could not list memory files' });
457
+ }
458
+ });
459
+
460
+ // GET /memory/file?path=... — fetch ONE of the AUTHENTICATED builder's files
461
+ // (content included). Single query (fetchMemoryFile is one SELECT). 404 if
462
+ // this builder has no file at that path. We use a ?path= query rather than a
463
+ // path-segment param because the stored keys are relative paths that may
464
+ // contain '/' (e.g. 'sub/dir/notes.md'); a single :path segment can't carry a
465
+ // slash, and a wildcard segment re-opens the traversal surface that
466
+ // assertValidPath exists to close. The query value is validated by
467
+ // assertValidPath (no leading '/', no '..', no NUL, no backslash) before it
468
+ // ever reaches the store.
469
+ //
470
+ // Auth: any authenticated builder. Self-scoped: owner is req.builder.id.
471
+ //
472
+ // Response 200:
473
+ // { builder_id, path, content, sha256, byte_len, mtime, updated_at, created_at }
474
+ router.get('/memory/file', auth.requireBuilder, readRateLimit, async (req, res) => {
475
+ let builderId;
476
+ try {
477
+ builderId = assertSelf(req);
478
+ } catch (_e) {
479
+ return res.fail('unauthenticated', 401);
480
+ }
481
+ const path = typeof req.query.path === 'string' ? req.query.path : null;
482
+ if (path === null) {
483
+ return res.fail('bad_request', { status: 400, message: 'path query param is required', details: { code: 'BAD_PATH' } });
484
+ }
485
+ try {
486
+ memory.assertValidPath(path); // reuse the one canonical validator
487
+ } catch (e) {
488
+ return res.fail('bad_path', { status: 400, message: 'internal error' ? e.message : 'invalid path', details: { code: 'BAD_PATH' } });
489
+ }
490
+ try {
491
+ const row = await memory.fetchMemoryFile(builderId, path);
492
+ if (!row) {
493
+ return res.fail('not_found', 404, { path });
494
+ }
495
+ res.set('Cache-Control', 'no-store');
496
+ return res.json({
497
+ builder_id: builderId,
498
+ path: row.path,
499
+ content: row.content,
500
+ sha256: row.sha256,
501
+ byte_len: Number(row.byte_len),
502
+ mtime: row.mtime,
503
+ updated_at: row.updated_at,
504
+ created_at: row.created_at,
505
+ });
506
+ } catch (err) {
507
+ console.error('[gds] GET /memory/file', { builderId, path, err });
508
+ return res.fail('fetch_failed', { status: 500, message: 'could not fetch memory file' });
509
+ }
510
+ });
511
+
512
+ // DELETE /memory/file?path=... — the SELF delete affordance (ADR 0026 §6C).
513
+ // A builder removes ONE of their OWN files — INCLUDING a BFG-written
514
+ // dream/correction, which lives in this builder's keyspace. Owner is ALWAYS
515
+ // req.builder.id (assertSelf); there is no parameter to delete another
516
+ // builder's file, so this can't be a cross-builder delete. This is what makes
517
+ // a BFG write reversible by its recipient (the load-bearing consent property).
518
+ // Single-line opener so the route-rank auditor classifies it ('any-builder').
519
+ router.delete('/memory/file', auth.requireBuilder, readRateLimit, async (req, res) => {
520
+ // Query-param validation (R11/#1998): type-check `path`. strict:false tolerates
521
+ // incidental query params rather than reject; the handler enforces path
522
+ // presence/shape (bad_request/bad_path) below.
523
+ if (api.validateOrRespond(req, res, {}, { query: { path: { type: 'string' } }, strict: false })) return;
524
+ let builderId;
525
+ try { builderId = assertSelf(req); } catch (_e) { return res.fail('unauthenticated', 401); }
526
+ const path = typeof req.query.path === 'string' ? req.query.path : null;
527
+ if (path === null) {
528
+ return res.fail('bad_request', { status: 400, message: 'path query param is required', details: { code: 'BAD_PATH' } });
529
+ }
530
+ try {
531
+ memory.assertValidPath(path);
532
+ } catch (e) {
533
+ return res.fail('bad_path', { status: 400, message: 'internal error' ? e.message : 'invalid path', details: { code: 'BAD_PATH' } });
534
+ }
535
+ try {
536
+ const n = await memory.deleteMemoryFile(builderId, path);
537
+ if (n === 0) return res.fail('not_found', 404, { path });
538
+ return res.json({ deleted: true, builder_id: builderId, path });
539
+ } catch (err) {
540
+ console.error('[gds] DELETE /memory/file', { builderId, path, err });
541
+ return res.fail('delete_failed', { status: 500, message: 'could not delete memory file' });
542
+ }
543
+ });
544
+
545
+ // -----------------------------------------------------------------------
546
+ // ARCHON FORENSIC OVERRIDE (#276 / V3.R57) — the ONLY endpoints in this
547
+ // module that take an owner id from input. READ-ONLY, requireRank('archon'),
548
+ // and EVERY call is audit-logged (recordForensicRead) because the global
549
+ // audit middleware doesn't instrument GETs. This is the documented exception
550
+ // to the isolation invariant: an Archon can read another builder's memory for
551
+ // forensics, but the read leaves an immutable trail.
552
+ // -----------------------------------------------------------------------
553
+
554
+ // GET /memory/builders/:builderId/files — Archon lists ANOTHER builder's
555
+ // memory file metadata. Audit-logged.
556
+ router.get(
557
+ '/memory/builders/:builderId/files',
558
+ auth.requireBuilder,
559
+ auth.requireRank('archon'),
560
+ readRateLimit,
561
+ async (req, res) => {
562
+ const targetId = Number(req.params.builderId);
563
+ if (!Number.isInteger(targetId) || targetId <= 0) {
564
+ return res.fail('bad_builder_id', 400);
565
+ }
566
+ const route = '/api/bongos/memory/builders/:builderId/files';
567
+ let rows;
568
+ try {
569
+ rows = await memory.listMemoryFiles(targetId);
570
+ } catch (err) {
571
+ console.error('[gds] GET /memory/builders/:builderId/files', { targetId, err });
572
+ return res.fail('list_failed', { status: 500, message: 'could not list memory files' });
573
+ }
574
+ // FAIL CLOSED: the audit row must land durably BEFORE we return any of the
575
+ // target builder's data. A list always "succeeds" (empty list is a valid
576
+ // result), so the audited responseStatus is 200. If the audit insert
577
+ // throws, withhold the data and return 500 — no forensic read is ever
578
+ // served without a durable audit row.
579
+ try {
580
+ await recordForensicRead(req, { route, targetBuilderId: targetId, target: 'list', responseStatus: 200 });
581
+ } catch (auditErr) {
582
+ console.error('[gds] forensic-read audit insert failed (read withheld)', {
583
+ route, targetId, err: auditErr && auditErr.message ? auditErr.message : String(auditErr),
584
+ });
585
+ return res.fail('audit_failed', { status: 500, message: 'forensic read not served: audit could not be recorded' });
586
+ }
587
+ res.set('Cache-Control', 'no-store');
588
+ return res.json({
589
+ builder_id: targetId,
590
+ forensic: true,
591
+ count: rows.length,
592
+ files: rows.map((r) => ({
593
+ path: r.path,
594
+ sha256: r.sha256,
595
+ byte_len: Number(r.byte_len),
596
+ mtime: r.mtime,
597
+ updated_at: r.updated_at,
598
+ created_at: r.created_at,
599
+ })),
600
+ });
601
+ }
602
+ );
603
+
604
+ // GET /memory/builders/:builderId/file?path=... — Archon reads ONE of
605
+ // ANOTHER builder's files (content included). Audit-logged. 404 if the
606
+ // target has no file at that path.
607
+ router.get(
608
+ '/memory/builders/:builderId/file',
609
+ auth.requireBuilder,
610
+ auth.requireRank('archon'),
611
+ readRateLimit,
612
+ async (req, res) => {
613
+ const targetId = Number(req.params.builderId);
614
+ if (!Number.isInteger(targetId) || targetId <= 0) {
615
+ return res.fail('bad_builder_id', 400);
616
+ }
617
+ const path = typeof req.query.path === 'string' ? req.query.path : null;
618
+ if (path === null) {
619
+ return res.fail('bad_request', { status: 400, message: 'path query param is required', details: { code: 'BAD_PATH' } });
620
+ }
621
+ try {
622
+ memory.assertValidPath(path);
623
+ } catch (e) {
624
+ return res.fail('bad_path', { status: 400, message: 'internal error' ? e.message : 'invalid path', details: { code: 'BAD_PATH' } });
625
+ }
626
+ const route = '/api/bongos/memory/builders/:builderId/file';
627
+ let row;
628
+ try {
629
+ row = await memory.fetchMemoryFile(targetId, path);
630
+ } catch (err) {
631
+ console.error('[gds] GET /memory/builders/:builderId/file', { targetId, path, err });
632
+ return res.fail('fetch_failed', { status: 500, message: 'could not fetch memory file' });
633
+ }
634
+ // FAIL CLOSED: audit the look — logged even if the path is a 404, because
635
+ // "the Archon TRIED to read X" is the forensic fact we want — and the row
636
+ // must land durably BEFORE we return data. responseStatus reflects the
637
+ // ACTUAL outcome (200 if the file existed, 404 if absent), not a hardcoded
638
+ // 200. If the audit insert throws, withhold data and return 500.
639
+ const outcomeStatus = row ? 200 : 404;
640
+ try {
641
+ await recordForensicRead(req, { route, targetBuilderId: targetId, target: path, responseStatus: outcomeStatus });
642
+ } catch (auditErr) {
643
+ console.error('[gds] forensic-read audit insert failed (read withheld)', {
644
+ route, targetId, path, err: auditErr && auditErr.message ? auditErr.message : String(auditErr),
645
+ });
646
+ return res.fail('audit_failed', { status: 500, message: 'forensic read not served: audit could not be recorded' });
647
+ }
648
+ if (!row) {
649
+ return res.fail('not_found', 404, { builder_id: targetId, path });
650
+ }
651
+ res.set('Cache-Control', 'no-store');
652
+ return res.json({
653
+ builder_id: targetId,
654
+ forensic: true,
655
+ path: row.path,
656
+ content: row.content,
657
+ sha256: row.sha256,
658
+ byte_len: Number(row.byte_len),
659
+ mtime: row.mtime,
660
+ updated_at: row.updated_at,
661
+ created_at: row.created_at,
662
+ });
663
+ }
664
+ );
665
+
666
+ // -----------------------------------------------------------------------
667
+ // BFG CROSS-BUILDER READ (#444 / ADR 0026 §6B) — the "read the room"
668
+ // substrate. Reachable ONLY by the BFG service principal (requireBfgPrincipal,
669
+ // an IDENTITY gate — NOT Archon rank), same audited fail-closed posture as the
670
+ // Archon forensic reads above but with a distinct audit action
671
+ // ('bfg_memory_read'). Lets the BFG read any builder's memory to SCORE
672
+ // interaction quality and (in 6B.2) extract transferable practice. Openers are
673
+ // single-line so the route-rank auditor detects + classifies them.
674
+ // -----------------------------------------------------------------------
675
+
676
+ router.get('/memory/bfg/builders/:builderId/files',
677
+ auth.requireBuilder,
678
+ auth.requireBfgPrincipal,
679
+ readRateLimit,
680
+ async (req, res) => {
681
+ const targetId = Number(req.params.builderId);
682
+ if (!Number.isInteger(targetId) || targetId <= 0) {
683
+ return res.fail('bad_builder_id', 400);
684
+ }
685
+ const route = '/api/bongos/memory/bfg/builders/:builderId/files';
686
+ let rows;
687
+ try {
688
+ rows = await memory.listMemoryFiles(targetId);
689
+ } catch (err) {
690
+ console.error('[gds] GET /memory/bfg/builders/:builderId/files', { targetId, err });
691
+ return res.fail('list_failed', { status: 500, message: 'could not list memory files' });
692
+ }
693
+ // FAIL CLOSED: the audit row must land before any of the target's data is
694
+ // served (same invariant as the Archon forensic read).
695
+ try {
696
+ await recordForensicRead(req, { route, targetBuilderId: targetId, target: 'list', responseStatus: 200, action: 'bfg_memory_read' });
697
+ } catch (auditErr) {
698
+ console.error('[gds] bfg-read audit insert failed (read withheld)', { route, targetId, err: auditErr && auditErr.message ? auditErr.message : String(auditErr) });
699
+ return res.fail('audit_failed', { status: 500, message: 'read not served: audit could not be recorded' });
700
+ }
701
+ res.set('Cache-Control', 'no-store');
702
+ return res.json({
703
+ builder_id: targetId,
704
+ bfg_read: true,
705
+ count: rows.length,
706
+ files: rows.map((r) => ({ path: r.path, sha256: r.sha256, byte_len: Number(r.byte_len), mtime: r.mtime, updated_at: r.updated_at, created_at: r.created_at })),
707
+ });
708
+ }
709
+ );
710
+
711
+ router.get('/memory/bfg/builders/:builderId/file',
712
+ auth.requireBuilder,
713
+ auth.requireBfgPrincipal,
714
+ readRateLimit,
715
+ async (req, res) => {
716
+ const targetId = Number(req.params.builderId);
717
+ if (!Number.isInteger(targetId) || targetId <= 0) {
718
+ return res.fail('bad_builder_id', 400);
719
+ }
720
+ const path = typeof req.query.path === 'string' ? req.query.path : null;
721
+ if (path === null) {
722
+ return res.fail('bad_request', { status: 400, message: 'path query param is required', details: { code: 'BAD_PATH' } });
723
+ }
724
+ try {
725
+ memory.assertValidPath(path);
726
+ } catch (e) {
727
+ return res.fail('bad_path', { status: 400, message: 'internal error' ? e.message : 'invalid path', details: { code: 'BAD_PATH' } });
728
+ }
729
+ const route = '/api/bongos/memory/bfg/builders/:builderId/file';
730
+ let row;
731
+ try {
732
+ row = await memory.fetchMemoryFile(targetId, path);
733
+ } catch (err) {
734
+ console.error('[gds] GET /memory/bfg/builders/:builderId/file', { targetId, path, err });
735
+ return res.fail('fetch_failed', { status: 500, message: 'could not fetch memory file' });
736
+ }
737
+ const outcomeStatus = row ? 200 : 404;
738
+ try {
739
+ await recordForensicRead(req, { route, targetBuilderId: targetId, target: path, responseStatus: outcomeStatus, action: 'bfg_memory_read' });
740
+ } catch (auditErr) {
741
+ console.error('[gds] bfg-read audit insert failed (read withheld)', { route, targetId, path, err: auditErr && auditErr.message ? auditErr.message : String(auditErr) });
742
+ return res.fail('audit_failed', { status: 500, message: 'read not served: audit could not be recorded' });
743
+ }
744
+ if (!row) return res.fail('not_found', 404, { builder_id: targetId, path });
745
+ res.set('Cache-Control', 'no-store');
746
+ return res.json({
747
+ builder_id: targetId,
748
+ bfg_read: true,
749
+ path: row.path,
750
+ content: row.content,
751
+ sha256: row.sha256,
752
+ byte_len: Number(row.byte_len),
753
+ mtime: row.mtime,
754
+ updated_at: row.updated_at,
755
+ created_at: row.created_at,
756
+ });
757
+ }
758
+ );
759
+
760
+ // -----------------------------------------------------------------------
761
+ // BFG CROSS-BUILDER WRITE (#446 / ADR 0026 §6C) — the FIRST cross-builder
762
+ // memory write path, the deliberate bounded exception to ADR 0016. Reachable
763
+ // ONLY by the BFG service principal (requireBfgPrincipal — an IDENTITY gate on
764
+ // system_role='bfg', NOT a rank gate, so an Archon/stolen token is rejected),
765
+ // ONLY when the kill-switch BFG_WRITE_ENABLED is on, and EVERY write lands a
766
+ // fail-closed audit row in the SAME transaction as the write (memory.bfgWrite-
767
+ // MemoryFile). Writes are namespaced (bfg/<kind>-<slug>.md) + self-announcing
768
+ // (frontmatter author: BFG) so a BFG note can never masquerade as the
769
+ // builder's own. The builder can always see and delete it.
770
+ //
771
+ // Rate limiting: this is a small write (one file), so the GLOBAL per-session/
772
+ // per-IP write limiter (middleware/rate-limit.js, applied to every GDS write)
773
+ // already throttles a runaway loop. Unlike /memory/sync — which carries up to
774
+ // 50 MB and needed a tighter dedicated cap — this endpoint doesn't warrant its
775
+ // own limiter, especially as it's principal-only + kill-switched.
776
+ //
777
+ // POST /memory/builders/:builderId/bfg-write
778
+ // body: { kind: 'dream'|'correction', slug, title?, body_md, rationale }
779
+ // 201 → { ok, builder_id, path, kind, author:'BFG', audit_id, sha256, byte_len, updated_at }
780
+ // Declaration opener kept on ONE line (route path beside router.post) so the
781
+ // deterministic route-rank auditor (src/bongos/route-rank-check.js) detects +
782
+ // classifies it; it reads requireBuilder in the chain below → 'any-builder',
783
+ // and requireBfgPrincipal narrows that to the principal alone.
784
+ router.post('/memory/builders/:builderId/bfg-write',
785
+ auth.requireBuilder,
786
+ auth.requireBfgPrincipal,
787
+ async (req, res) => {
788
+ // Kill-switch first: a disabled write path must answer the same to the
789
+ // principal as to anyone else — the capability simply isn't on.
790
+ if (!bfgWriteEnabled()) {
791
+ return res.fail('bfg_write_disabled', { status: 503, message: 'the BFG cross-builder write path is disabled (BFG_WRITE_ENABLED is not set)' });
792
+ }
793
+ const targetId = Number(req.params.builderId);
794
+ if (!Number.isInteger(targetId) || targetId <= 0) {
795
+ return res.fail('bad_builder_id', 400);
796
+ }
797
+ if (String(targetId) === String(req.builder.id)) {
798
+ return res.fail('bfg_cannot_write_self', 400);
799
+ }
800
+
801
+ if (api.validateOrRespond(req, res, {
802
+ kind: { type: 'string' },
803
+ slug: { type: 'string' },
804
+ title: { type: 'string' },
805
+ body_md: { type: 'string' },
806
+ rationale: { type: 'string' },
807
+ karma_delta: {}, // non-positive integer or null; validated below (correction-only)
808
+ })) return;
809
+ const { kind, slug, title, body_md, rationale } = req.body || {};
810
+
811
+ // Construct the attributed file (pure) — throws BAD_BFG_* on bad input, so
812
+ // the mandatory frontmatter can never be omitted.
813
+ let file;
814
+ try {
815
+ file = memory.buildBfgMemoryFile({
816
+ kind,
817
+ slug,
818
+ title,
819
+ body_md,
820
+ rationale,
821
+ sourceBuilder: req.builder.github_login,
822
+ targetBuilderId: targetId,
823
+ });
824
+ } catch (e) {
825
+ return res.fail('bad_bfg_input', { status: 400, message: 'internal error', details: { code: e && e.code } });
826
+ }
827
+
828
+ // Target must be a real, non-system builder.
829
+ let target;
830
+ try {
831
+ target = await db.getBuilderById(targetId);
832
+ } catch (err) {
833
+ console.error('[gds] bfg-write target lookup', { targetId, err });
834
+ return res.fail('lookup_failed', 500);
835
+ }
836
+ if (!target) return res.fail('target_not_found', 404);
837
+ if (target.system_role) return res.fail('target_is_system_principal', 400);
838
+
839
+ // 6C.2 — a CORRECTION may carry a negative karma delta alongside its note
840
+ // (ADR 0026 §6C). It is only valid on a correction, and MUST be
841
+ // non-positive: the BFG corrects by docking, never by rewarding (a reward
842
+ // is a different, non-BFG mechanism). A dream never carries karma.
843
+ let karma = null;
844
+ if (req.body.karma_delta != null) {
845
+ if (kind !== 'correction') {
846
+ return res.fail('karma_delta_not_allowed', { status: 400, message: 'karma_delta is only valid for kind=correction' });
847
+ }
848
+ const kd = Number(req.body.karma_delta);
849
+ if (!Number.isInteger(kd) || kd > 0) {
850
+ return res.fail('bad_karma_delta', { status: 400, message: 'karma_delta must be a non-positive integer (corrections dock, never reward)' });
851
+ }
852
+ if (kd !== 0) karma = { delta: kd, reason: 'bfg_correction', grantedBy: req.builder.id };
853
+ }
854
+
855
+ const action = kind === 'correction' ? 'bfg_correction_write' : 'bfg_dream_write';
856
+ try {
857
+ const result = await memory.bfgWriteMemoryFile({
858
+ targetBuilderId: targetId,
859
+ path: file.path,
860
+ content: file.content,
861
+ audit: {
862
+ principalId: req.builder.id,
863
+ route: '/api/bongos/memory/builders/:builderId/bfg-write',
864
+ action,
865
+ kind,
866
+ rationale: String(rationale).slice(0, 500),
867
+ responseStatus: 201,
868
+ ip: req.ip || (req.socket && req.socket.remoteAddress) || null,
869
+ userAgent: (req.headers && req.headers['user-agent']) || null,
870
+ },
871
+ karma,
872
+ });
873
+ return res.status(201).json({
874
+ ok: true,
875
+ builder_id: targetId,
876
+ path: result.row.path,
877
+ kind,
878
+ author: 'BFG',
879
+ audit_id: result.audit_id,
880
+ karma_id: result.karma_id ?? null,
881
+ sha256: result.row.sha256,
882
+ byte_len: Number(result.row.byte_len),
883
+ updated_at: result.row.updated_at,
884
+ });
885
+ } catch (e) {
886
+ const code = e && e.code;
887
+ if (code === 'FILE_TOO_LARGE' || code === 'MEMORY_QUOTA_EXCEEDED') {
888
+ return res.fail('quota_exceeded', 413, { code });
889
+ }
890
+ if (code === 'NOT_BFG_NAMESPACE') {
891
+ // Unreachable via THIS route (buildBfgMemoryFile always yields a bfg/
892
+ // path), but bfgWriteMemoryFile is exported — this keeps the helper's
893
+ // own assertBfgPath guard mapped to a clean 400 for any direct caller.
894
+ return res.fail('bad_path', 400, { code });
895
+ }
896
+ console.error('[gds] POST /memory/builders/:builderId/bfg-write', e);
897
+ return res.fail('bfg_write_failed', 500);
898
+ }
899
+ }
900
+ );
901
+
902
+ return router;
903
+ };
904
+
905
+ // Exposed for the test suite — pure helpers + the middleware factories, no
906
+ // router state. Tests exercise the isolation invariant (assertSelf) and the
907
+ // forensic audit shape directly, without standing up Express or a DB.
908
+ module.exports._internals = {
909
+ assertSelf,
910
+ recordForensicRead,
911
+ readRateLimit,
912
+ syncRateLimit,
913
+ READ_RATE_LIMIT,
914
+ SYNC_RATE_LIMIT,
915
+ bfgWriteEnabled,
916
+ };