@bongos/core 1.19.715 → 1.19.725

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 (429) hide show
  1. package/.bongos-core.json +491 -421
  2. package/.claude/skills/backlog-review/SKILL.md +3 -3
  3. package/.claude/skills/blocker-review/SKILL.md +1 -1
  4. package/.claude/skills/blocker-solve/SKILL.md +2 -2
  5. package/.claude/skills/builder-reauth/SKILL.md +1 -1
  6. package/.claude/skills/builder-redteam/SKILL.md +1 -1
  7. package/.claude/skills/builder-ship/SKILL.md +1 -1
  8. package/.claude/skills/builder-start/SKILL.md +1 -1
  9. package/.claude/skills/design/SKILL.md +2 -2
  10. package/.claude/skills/feedback/SKILL.md +1 -1
  11. package/.claude/skills/goal-create/SKILL.md +1 -1
  12. package/.claude/skills/goal-review/SKILL.md +2 -2
  13. package/.claude/skills/idea-triage/SKILL.md +5 -5
  14. package/.claude/skills/merge-mode/SKILL.md +4 -4
  15. package/.claude/skills/new-project/SKILL.md +17 -17
  16. package/.claude/skills/planning-session/SKILL.md +11 -11
  17. package/.claude/skills/recall/SKILL.md +2 -2
  18. package/.claude/skills/strand-fix/SKILL.md +1 -1
  19. package/.devcontainer/README.md +2 -2
  20. package/CONTRIBUTING.md +2 -2
  21. package/GOVERNANCE.md +4 -4
  22. package/clients/bongos-client/README.md +1 -1
  23. package/clients/bongos-client/bongos-client.global.js +4 -0
  24. package/clients/bongos-client/index.cjs +4 -0
  25. package/clients/bongos-client/index.d.ts +5 -0
  26. package/clients/bongos-client/index.mjs +4 -0
  27. package/config/branding.neutral.json +4 -2
  28. package/docs/adr/0001-stack-choice-single-vps.md +2 -2
  29. package/docs/adr/0002-digitalocean-over-hetzner.md +1 -1
  30. package/docs/adr/0008-google-chat-oauth-user-auth.md +1 -1
  31. package/docs/adr/0009-autotile-architecture.md +1 -1
  32. package/docs/adr/0011-terrain-id-rendering-model.md +3 -3
  33. package/docs/adr/0013-paths-as-object-overlays-with-autotile-dispatch.md +1 -1
  34. package/docs/adr/0014-families-canonical-over-vocabulary.md +1 -1
  35. package/docs/adr/0015-task-dependencies-and-auto-promotion.md +1 -1
  36. package/docs/adr/0016-trust-boundary-server-enforced-permissions.md +1 -1
  37. package/docs/adr/0018-three-rank-model-goes-live.md +3 -3
  38. package/docs/adr/0020-security-model-public-stub.md +9 -9
  39. package/docs/adr/0021-per-builder-skill-model-preferences.md +2 -2
  40. package/docs/adr/0022-secrets-policy.md +5 -5
  41. package/docs/adr/0024-cloneable-repo-local-first-memory.md +3 -3
  42. package/docs/adr/0024-multi-agent-system-architecture.md +10 -10
  43. package/docs/adr/0025-offsite-backup-vendor-digitalocean-spaces.md +1 -1
  44. package/docs/adr/0025-structured-criterion-task-link.md +1 -1
  45. package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +13 -13
  46. package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +14 -14
  47. package/docs/adr/0029-status-page-outage-resilience.md +1 -1
  48. package/docs/adr/0030-builders-hall-live-updates.md +1 -1
  49. package/docs/adr/0031-cloud-dev-environments-for-builders.md +7 -7
  50. package/docs/adr/0032-discord-mirror-of-gds.md +3 -3
  51. package/docs/adr/0033-discord-bot-service-principal.md +4 -4
  52. package/docs/adr/0034-thetes-graduated-newcomer-rank.md +1 -1
  53. package/docs/adr/0035-builder-onboarding-three-paths.md +4 -4
  54. package/docs/adr/0036-builders-subdomain-and-ui-reorg.md +1 -1
  55. package/docs/adr/0037-discord-channel-management.md +1 -1
  56. package/docs/adr/0038-chromebook-ttyd-cloudflare-tunnel.md +5 -5
  57. package/docs/adr/0039-setup-first-onboarding-ux.md +6 -6
  58. package/docs/adr/0040-remote-control-default-browser-onramp.md +3 -3
  59. package/docs/adr/0041-temporary-grader-bypass-killswitch.md +2 -2
  60. package/docs/adr/0042-builder-self-deploy-ci-auto-merge.md +5 -5
  61. package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +2 -2
  62. package/docs/adr/0044-mediterranean-palette-replacement.md +1 -1
  63. package/docs/adr/0044-per-box-live-game-preview.md +1 -1
  64. package/docs/adr/0046-sandbox-first-review-gate.md +1 -1
  65. package/docs/adr/0047-discord-bugs-intake-anti-abuse.md +1 -1
  66. package/docs/adr/0049-split-parallel-safety-contract.md +1 -1
  67. package/docs/adr/0050-device-flow-admission-invite-gated-by-default.md +2 -2
  68. package/docs/adr/0051-full-session-transcript-corpus.md +2 -2
  69. package/docs/adr/0052-sandbox-for-everyone-game-only-preview.md +1 -1
  70. package/docs/adr/0053-scoped-dev-box-session.md +1 -1
  71. package/docs/adr/0054-session-token-reward-cost-plus.md +7 -7
  72. package/docs/adr/0055-server-mediated-branch-publish.md +2 -2
  73. package/docs/adr/0056-prod-deploy-script-mirror-and-healthcheck-retry.md +3 -3
  74. package/docs/adr/0057-container-cost-ledger.md +1 -1
  75. package/docs/adr/0057-discord-archon-approval-channels.md +2 -2
  76. package/docs/adr/0058-automatic-gate-surface-gatekeeper.md +6 -6
  77. package/docs/adr/0059-single-approval-remove-devbox-approval-gate.md +11 -11
  78. package/docs/adr/0060-gds-retrieval-layer.md +3 -3
  79. package/docs/adr/0061-context-layer-decomposition.md +2 -2
  80. package/docs/adr/0062-medusa-instance-model-decoupling.md +5 -5
  81. package/docs/adr/0063-generated-repo-map-symbol-skeleton.md +2 -2
  82. package/docs/adr/0064-rename-medusa-to-cloud-bongos.md +3 -3
  83. package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +5 -5
  84. package/docs/adr/0066-generated-file-map-sections.md +2 -2
  85. package/docs/adr/0067-session-start-fast-forward-pull.md +1 -1
  86. package/docs/adr/0068-shared-art-key-for-newcomers.md +1 -1
  87. package/docs/adr/0069-hall-live-channel-sse-decouple.md +3 -3
  88. package/docs/adr/0070-deterministic-session-card-delivery.md +1 -1
  89. package/docs/adr/0071-box-confirm-before-destroyed-and-drift-reconcile.md +2 -2
  90. package/docs/adr/0072-bongos-app-mac-signed-first-windows-deferred.md +2 -2
  91. package/docs/adr/0072-dev-box-code-staleness-visibility.md +2 -2
  92. package/docs/adr/0073-builder-needs-signal-and-byok-gemini-key.md +3 -3
  93. package/docs/adr/0073-secrets-scan-exclude-uri-detector.md +2 -2
  94. package/docs/adr/0076-in-session-deterministic-compaction.md +2 -2
  95. package/docs/adr/0077-content-addressed-llm-cache.md +2 -2
  96. package/docs/adr/0078-deterministic-first-cron.md +3 -3
  97. package/docs/adr/0079-value-gated-autonomous-engagement.md +2 -2
  98. package/docs/adr/0080-llm-cache-api-transport-and-trust-model.md +3 -3
  99. package/docs/adr/0081-tool-agnostic-design-layer.md +3 -3
  100. package/docs/adr/0082-server-side-merge-conflict-auto-resolution.md +2 -2
  101. package/docs/adr/0083-modular-architecture-strangler-build-plan.md +3 -3
  102. package/docs/adr/0085-remove-default-per-builder-spend-cap.md +2 -2
  103. package/docs/adr/0086-goal-scoped-work-hierarchy.md +15 -15
  104. package/docs/adr/0087-bongos-app-architecture-and-handoff-contract.md +5 -5
  105. package/docs/adr/0087-compete-on-governance-not-tooling-cursor.md +1 -1
  106. package/docs/adr/0088-consistent-character-generative-animation.md +5 -5
  107. package/docs/adr/0089-modular-multi-model-adversarial-grader.md +15 -15
  108. package/docs/adr/0090-metic-task-authoring.md +8 -8
  109. package/docs/adr/0091-bounding-the-kernel-and-db-carve.md +3 -3
  110. package/docs/adr/0092-hosting-module-and-on-prem-target.md +11 -11
  111. package/docs/adr/0093-tranche-2-core-carve-sequence.md +5 -5
  112. package/docs/adr/0094-repo-atlas-self-drawing-codebase-map.md +1 -1
  113. package/docs/adr/0095-borrowed-memory-and-retrieval-concepts.md +6 -6
  114. package/docs/adr/0095-cross-agent-context-management.md +5 -5
  115. package/docs/adr/0096-require-reward-before-workable.md +5 -5
  116. package/docs/adr/0097-one-active-claim-per-session-and-worktree-binding.md +2 -2
  117. package/docs/adr/0097-retroactive-reward-backfill.md +4 -4
  118. package/docs/adr/0098-public-mirror-publish-manifest.md +3 -3
  119. package/docs/adr/0099-delayed-redacted-mirror-export.md +1 -1
  120. package/docs/adr/0100-two-repo-instance-model.md +6 -6
  121. package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +9 -9
  122. package/docs/adr/0102-finish-bounding-the-kernel.md +1 -1
  123. package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +6 -6
  124. package/docs/adr/0103-gdsv4-bongos-consolidation.md +3 -3
  125. package/docs/adr/0104-trust-gds-api-channel-in-auto-mode.md +3 -3
  126. package/docs/adr/0105-instance-seed-migrations-out-of-core.md +2 -2
  127. package/docs/adr/0106-goal-owner-manager-authority.md +2 -2
  128. package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +6 -6
  129. package/docs/adr/0108-instance-composition-configurable-root.md +6 -6
  130. package/docs/adr/0109-self-describing-openapi-and-hosted-docs.md +3 -3
  131. package/docs/adr/0110-untrack-whole-file-generated-nav-docs.md +1 -1
  132. package/docs/adr/0111-instance-hosting-provisioning-module.md +18 -18
  133. package/docs/adr/0112-private-goals-invitations-join-requests.md +7 -7
  134. package/docs/adr/0113-sandbox-studio-descope-superseded-by-0044-0052.md +1 -1
  135. package/docs/adr/0114-instance-docs-discovery-contract.md +4 -4
  136. package/docs/adr/0115-scheduled-routines-portability.md +1 -1
  137. package/docs/adr/0117-api-versioning-and-deprecation-policy.md +1 -1
  138. package/docs/adr/0118-api-contract-source-of-truth-code-first.md +1 -1
  139. package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +2 -2
  140. package/docs/adr/0121-greenfield-vs-brownfield-onboarding-adopt-existing-repo.md +1 -1
  141. package/docs/adr/0122-onboarding-diagram-renders-deploy-rendered-off-git.md +3 -3
  142. package/docs/adr/0123-box-idle-sweep-autosave-before-destroy.md +1 -1
  143. package/docs/adr/0124-typed-api-response-bodies.md +2 -2
  144. package/docs/adr/0125-control-plane-scaffolds-standalone-instance-repo.md +2 -2
  145. package/docs/adr/0126-dedicated-cloudbongos-control-plane-droplet.md +3 -3
  146. package/docs/adr/0127-cross-box-fleet-observability.md +1 -1
  147. package/docs/adr/0128-provisioning-runner-privilege-and-db-auth-model.md +3 -3
  148. package/docs/adr/0129-cloudbongos-control-plane-pull-deploy.md +4 -4
  149. package/docs/adr/0130-provision-remote-exec-over-ssh.md +4 -4
  150. package/docs/adr/0131-rank-scoped-skill-visibility.md +2 -2
  151. package/docs/adr/0132-cohost-fleet-control-plane-migration.md +2 -2
  152. package/docs/adr/0133-github-app-manifest-onboarding-signin.md +1 -1
  153. package/docs/adr/0134-private-first-npm-distribution.md +3 -3
  154. package/docs/adr/0135-module-upstream-submission-interim-queue.md +1 -1
  155. package/docs/adr/0136-update-channel-subscription-policy.md +2 -2
  156. package/docs/adr/0137-upstream-publish-policy-manual-owner-gated.md +6 -6
  157. package/docs/adr/0138-bundled-module-deps-declared-per-module.md +4 -4
  158. package/docs/adr/0142-project-context-is-prose-only.md +2 -2
  159. package/docs/adr/0143-onboarding-repo-picker-oauth-scope.md +2 -2
  160. package/docs/adr/0144-devbox-rehome-onto-cloudbongos-plane.md +2 -2
  161. package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +2 -2
  162. package/docs/adr/0146-configurable-reward-mode-cost-plus-only.md +4 -4
  163. package/docs/adr/0147-standalone-live-docs-regen.md +1 -1
  164. package/docs/adr/0149-bongos-dev-local-launcher.md +1 -1
  165. package/docs/adr/0150-box-first-boot-bringup-vendored-instances.md +3 -3
  166. package/docs/adr/0151-governance-permissions-as-atom-ranks-as-roles.md +20 -20
  167. package/docs/adr/0152-landing-gate-served-by-the-core-app.md +2 -2
  168. package/docs/adr/0152-metic-task-abandonment.md +7 -7
  169. package/docs/adr/0153-sequential-chain-runner.md +1 -1
  170. package/docs/adr/0154-goal-owner-criterion-authoring.md +7 -7
  171. package/docs/adr/0155-adopt-private-repo-widen-oauth-scope.md +2 -2
  172. package/docs/adr/0156-user-set-display-name-wins.md +1 -1
  173. package/docs/adr/0157-archon-is-rank-and-identity-only.md +10 -10
  174. package/docs/adr/0158-choke-point-verdict-taxonomy.md +1 -1
  175. package/docs/adr/0159-merge-gate-reconciliation.md +1 -1
  176. package/docs/adr/0160-main-audit-repo-verifiable-caller.md +1 -1
  177. package/docs/adr/0161-publish-on-merge.md +2 -2
  178. package/docs/adr/0163-legacy-display-name-resolution.md +1 -1
  179. package/docs/adr/0165-instances-hookless-by-default.md +1 -1
  180. package/docs/adr/0166-module-maintenance-and-deprecation-policy.md +3 -3
  181. package/docs/adr/0167-gate-trust-link-agpl-default.md +2 -2
  182. package/docs/adr/0167-module-catalog-source-vs-provenance.md +3 -3
  183. package/docs/adr/0169-bongos-serve-server-bring-up.md +1 -1
  184. package/docs/adr/0171-profile-rollup-join-and-consent.md +1 -1
  185. package/docs/adr/0172-editable-rank-roles-substrate-axis.md +2 -2
  186. package/docs/adr/0172-per-craft-compensation-ideator-credit-lane.md +4 -4
  187. package/docs/adr/0173-legacy-multi-ship-shortfall-not-backfilled.md +1 -1
  188. package/docs/adr/0174-government-terminology-ranks-not-roles.md +1 -1
  189. package/docs/adr/0177-goal-membership-kind.md +3 -3
  190. package/docs/adr/0178-the-copy-desk-flag-and-queue.md +1 -1
  191. package/docs/adr/0179-board-membership-predicate-grammar.md +1 -1
  192. package/docs/adr/0181-teardown-state-gate-and-address-retention.md +1 -1
  193. package/docs/adr/0182-project-visibility-two-axes.md +1 -1
  194. package/docs/adr/0183-criteria-close-themselves.md +1 -1
  195. package/docs/adr/0184-ship-requires-an-assurance.md +1 -1
  196. package/docs/adr/0185-spark-handoff-credit-split.md +1 -1
  197. package/docs/adr/0186-per-project-idea-templates.md +1 -1
  198. package/docs/adr/0187-collab-four-decisions.md +1 -1
  199. package/docs/adr/0187-oauth-handshake-cookie-lifetime.md +1 -1
  200. package/docs/adr/0188-patrol-runs-on-ci-not-the-server.md +2 -2
  201. package/docs/adr/0189-baseline-floors-and-the-freeze-detector.md +2 -2
  202. package/docs/adr/0191-majority-and-the-author-who-may-not-decide-alone.md +1 -1
  203. package/docs/adr/0192-a-category-orients-and-authorises-nothing.md +2 -2
  204. package/docs/adr/0193-pause-task-scoped-box-slices.md +1 -1
  205. package/docs/adr/0195-adr-numbers-are-checked-like-migration-numbers.md +2 -2
  206. package/docs/adr/0196-chrome-world-token-contract-and-the-landing-sheets.md +2 -2
  207. package/docs/adr/0197-the-ui-design-module.md +1 -1
  208. package/docs/adr/0198-third-party-skill-vendoring-policy.md +1 -1
  209. package/docs/adr/0203-doorway-changelog-out-of-module-api.md +2 -2
  210. package/docs/adr/0204-the-apex-is-dark-only.md +2 -2
  211. package/docs/adr/0205-federated-checkin-needs-the-hubs-own-signin-witness.md +1 -1
  212. package/docs/adr/0206-own-scoped-session-revocation-no-absolute-ceiling.md +1 -1
  213. package/docs/adr/0207-connections-implementation-departures-and-honest-limits.md +1 -1
  214. package/docs/adr/0208-application-lifecycle-the-handle-waits-and-the-vouch-has-no-minter.md +2 -2
  215. package/docs/adr/0209-one-budget-for-the-account-existence-oracle.md +1 -1
  216. package/docs/adr/0210-scouting-graduates-on-an-instance-asserted-owner-claim.md +1 -1
  217. package/docs/adr/0212-connections-on-the-profile-surface-and-addressing.md +1 -1
  218. package/docs/adr/0213-platform-terms-acceptance-is-the-hubs-own-witness.md +2 -2
  219. package/docs/adr/0215-the-recruiter-sliver-is-a-ceiling-not-a-step.md +6 -6
  220. package/docs/adr/0216-a-fake-pool-interpreter-must-fail-loudly-not-silently-ignore.md +2 -2
  221. package/docs/adr/0217-rank-gates-inviting-not-viewing.md +1 -1
  222. package/docs/adr/0218-the-apex-root-web-surface.md +2 -2
  223. package/docs/adr/0219-a-look-is-a-branding-pack-the-style-library.md +1 -1
  224. package/docs/adr/0220-an-in-house-rebuilt-skill-is-a-first-party-skill.md +1 -1
  225. package/docs/adr/0221-the-impeccable-method-is-one-skill-with-sub-command-playbooks.md +2 -2
  226. package/docs/adr/0222-the-image-family-under-the-hero-only-rule.md +2 -2
  227. package/docs/adr/0223-the-detector-is-two-tiers-in-the-kit.md +3 -3
  228. package/docs/adr/0224-module-skills-reach-the-core-checkout-as-untracked-self-ignored-copies.md +1 -1
  229. package/docs/adr/0225-privacy-suppresses-cross-project-surfaces-only.md +1 -1
  230. package/docs/adr/0226-one-stale-timer-reporting-degrades-enforcement-refuses.md +1 -1
  231. package/docs/adr/0227-a-task-network-map-is-refused-the-goal-graph-is-the-map.md +1 -1
  232. package/docs/adr/0230-a-look-skill-is-a-composition-grammar-the-palette-is-the-pack.md +1 -1
  233. package/docs/adr/0231-the-style-session-an-owner-authors-a-look-inside-the-contract.md +2 -2
  234. package/docs/adr/0232-rot-is-derived-not-swept-and-water-is-the-only-new-verb.md +2 -2
  235. package/docs/adr/0233-a-copy-proposal-is-a-task-carrying-a-patch.md +2 -2
  236. package/docs/adr/0234-idea-routing-capture-time-promotion-landing-matrix-homeless-inbox.md +2 -2
  237. package/docs/adr/0235-a-fix-category-is-kindbug-under-a-goal-no-column-required.md +11 -11
  238. package/docs/adr/0236-many-ideas-one-task-the-lineage-key-is-the-pair.md +5 -5
  239. package/docs/adr/0237-a-starter-bundle-is-a-preset-over-the-always-on-core-keyed-by-declared-type.md +5 -5
  240. package/docs/adr/0238-the-join-door-is-a-provisioning-settings-policy-key.md +1 -1
  241. package/docs/adr/0239-a-recommendation-is-a-table-that-carries-its-own-reason.md +4 -4
  242. package/docs/adr/0240-a-skipped-module-picker-stores-nothing-and-resolves-the-bundle-on-read.md +4 -4
  243. package/docs/adr/0241-the-artist-gate-is-a-per-project-deploy-gate-that-reads-a-state.md +1 -1
  244. package/docs/adr/0242-a-cascade-is-a-declaration-table-on-the-event-that-already-exists.md +2 -2
  245. package/docs/adr/0243-a-bundle-adjustment-belongs-to-the-bundle-not-the-advice-about-it.md +4 -4
  246. package/docs/adr/0244-a-catalog-search-is-a-view-over-the-rows-never-an-edit-to-the-selection.md +3 -3
  247. package/docs/adr/0245-the-way-back-into-an-optional-step-is-that-step-s-own-answer-route.md +7 -7
  248. package/docs/adr/0246-catalog-membership-is-a-projection-not-a-federation-side-effect.md +1 -1
  249. package/docs/adr/0247-the-join-door-is-one-composed-answer-and-dark-matter-has-no-live-verdict-yet.md +2 -2
  250. package/docs/adr/0248-a-hub-project-s-owner-is-the-provisioning-row-carried-on-a-port.md +3 -3
  251. package/docs/adr/0249-the-invite-step-is-the-done-panel-s-first-act-not-a-rail-step.md +3 -3
  252. package/docs/adr/0250-strict-versioning-the-version-boundary-is-the-scope-gate.md +6 -6
  253. package/docs/adr/0251-a-suggestion-of-who-to-invite-is-a-recruiting-surface-and-owes-the-opt-out.md +5 -5
  254. package/docs/adr/0252-the-self-hosted-visibility-report-is-a-pull-and-auto-appear-buys-a-retraction-lever.md +19 -19
  255. package/docs/adr/0253-the-takedown-lever-has-two-writers-and-provenance-is-a-column.md +2 -2
  256. package/docs/adr/0254-a-relayed-link-is-contained-downward-or-it-is-not-published.md +6 -6
  257. package/docs/adr/0255-a-public-lists-ordering-is-part-of-its-payload.md +2 -2
  258. package/docs/adr/0256-the-visibility-ceiling-is-the-sign-in-gate-too-and-the-reader-moves-to-the-composer.md +3 -3
  259. package/docs/adr/0257-auth-resolves-before-the-hall-mounts-anything.md +1 -1
  260. package/docs/adr/0258-the-public-cli-is-a-generated-client-package-not-the-published-core.md +1 -1
  261. package/docs/adr/0259-a-projects-departure-from-the-public-list-is-public.md +7 -7
  262. package/docs/adr/0260-the-application-is-the-consent-and-the-echo-is-the-gate.md +4 -4
  263. package/docs/adr/0261-a-preselect-always-carries-a-reason-the-bundle-summary-is-the-floor.md +1 -1
  264. package/docs/adr/0262-a-bug-never-lands-in-the-inbox.md +1 -1
  265. package/docs/adr/0263-how-a-version-closes.md +3 -3
  266. package/docs/adr/0264-the-ten-working-areas.md +3 -3
  267. package/docs/adr/0265-work-with-no-goal-and-the-order-the-cut-needs.md +5 -5
  268. package/docs/adr/0266-the-board-room-is-its-own-surface-reachable-by-whoever-may-vote.md +2 -2
  269. package/docs/adr/0267-unanimity-and-the-revise-and-re-sit-loop.md +1 -1
  270. package/docs/adr/0268-the-constitution-comes-from-two-roots.md +3 -3
  271. package/docs/adr/0269-the-cli-session-store-is-host-keyed-at-a-fixed-anchor.md +1 -1
  272. package/docs/adr/0270-rank-floor-derives-without-a-prediction.md +2 -2
  273. package/docs/adr/0271-the-ideator-surface-is-two-halves.md +3 -3
  274. package/docs/adr/0272-a-craft-is-an-identity-not-a-toolchain.md +4 -4
  275. package/docs/adr/0273-hall-nav-can-gate-on-a-permission.md +1 -1
  276. package/docs/adr/0274-one-kernel-three-role-packs.md +1 -1
  277. package/docs/adr/0275-one-source-for-a-written-role-responsibility.md +1 -1
  278. package/docs/adr/0276-the-skill-listing-budget-cannot-hold-every-trigger.md +1 -1
  279. package/docs/adr/0277-a-box-is-in-use-only-while-a-human-is-attached.md +1 -1
  280. package/docs/adr/0278-a-gated-project-still-takes-applications.md +2 -2
  281. package/docs/adr/0279-an-upgrade-is-proven-by-the-served-version-not-the-health-check.md +1 -1
  282. package/docs/adr/0280-the-unattended-lane-may-drive-a-co-tenant-because-the-roster-is-the-decision.md +1 -1
  283. package/docs/adr/0281-an-instance-identity-is-its-own-unix-account-and-pg-role.md +2 -2
  284. package/docs/adr/README.md +294 -294
  285. package/docs/api/openapi.json +72 -5
  286. package/docs/api-reference.md +8 -2
  287. package/docs/architecture.md +22 -22
  288. package/docs/branding-contract.md +5 -5
  289. package/docs/canonical-permissions.md +22 -22
  290. package/docs/design/hall-direction-v2.md +3 -3
  291. package/docs/design/landing-page-direction-v2.md +1 -1
  292. package/docs/design/projects-hub-direction-v2.md +1 -1
  293. package/docs/file-map.md +3 -1
  294. package/docs/module-api-changelog.md +36 -1
  295. package/docs/modules-contract.md +25 -25
  296. package/docs/onboarding/browser-terminal-guide.md +1 -1
  297. package/docs/onboarding/diagrams/README.md +3 -3
  298. package/docs/onboarding/drachmae-and-karma.md +13 -13
  299. package/docs/onboarding/primer.md +7 -7
  300. package/docs/packs/artist.md +1 -1
  301. package/docs/packs/ideator.md +1 -1
  302. package/docs/project-context.template.md +3 -3
  303. package/docs/recipes/builder-box-lifecycle.md +5 -5
  304. package/docs/recipes/claude-design-loop.md +1 -1
  305. package/docs/recipes/core-release-pipeline.md +55 -14
  306. package/docs/recipes/figma-design-loop.md +1 -1
  307. package/docs/recipes/gds-db-backup.md +2 -2
  308. package/docs/recipes/instance-deploy.md +2 -2
  309. package/docs/recipes/instance-service-restart.md +3 -3
  310. package/docs/recipes/local-dev.md +2 -2
  311. package/docs/recipes/managed-settings-remote-control.md +3 -3
  312. package/docs/recipes/memory-backup.md +4 -4
  313. package/docs/recipes/memory-sync-conflicts.md +2 -2
  314. package/docs/recipes/multi-builder-merge.md +2 -2
  315. package/docs/recipes/ops-gotchas.md +4 -4
  316. package/docs/recipes/overnight-builder.md +1 -1
  317. package/docs/recipes/packaging-the-core.md +6 -6
  318. package/docs/recipes/private-npm-distribution.md +5 -5
  319. package/docs/recipes/search-index-ingestion.md +3 -3
  320. package/docs/recipes/self-host.md +5 -5
  321. package/docs/recipes/server-bring-up.md +1 -1
  322. package/docs/recipes/ship-and-regrade.md +2 -2
  323. package/docs/recipes/standalone-live-docs.md +1 -1
  324. package/docs/recipes/ui-look-before-you-ship.md +1 -1
  325. package/docs/recipes/upgrading-the-core.md +2 -2
  326. package/docs/recipes/work-on-a-task-from-your-dev-box.md +6 -6
  327. package/docs/research/cursor-competitive-analysis.md +3 -3
  328. package/docs/research/llm-model-agnostic-and-cheaper-grader.md +7 -7
  329. package/migrations/core_240_artist_gate_indexes.sql +48 -0
  330. package/migrations/core_240_goal_members_drop_legacy_role.sql +79 -0
  331. package/modules/dev-box/app/src/vendor/bongos-client.cjs +4 -0
  332. package/modules/lifecycle/cascade.js +37 -9
  333. package/modules/lifecycle/db-claims.js +5 -2
  334. package/modules/lifecycle/db-goals.js +4 -3
  335. package/modules/lifecycle/db-overrides.js +117 -4
  336. package/modules/lifecycle/db-tasks.js +63 -0
  337. package/modules/lifecycle/migrations/lifecycle_001_goal_first_leads.sql +12 -4
  338. package/modules/lifecycle/module.json +2 -1
  339. package/modules/lifecycle/pg-constraint-fail.js +49 -0
  340. package/modules/lifecycle/routes/artist-gate.js +101 -0
  341. package/modules/lifecycle/routes/claims.js +13 -1
  342. package/modules/lifecycle/routes/lifecycle.js +6 -0
  343. package/modules/lifecycle/routes/task-write-routes.js +7 -0
  344. package/modules/lifecycle/routes/tasks.js +24 -0
  345. package/modules/lifecycle/task-classifier.js +10 -1
  346. package/modules/lifecycle/tests/cascade.mjs +43 -0
  347. package/modules/onboarding/approval-broadcast.js +27 -0
  348. package/modules/provisioning/provisioning.js +79 -3
  349. package/modules/provisioning/routes/provisioning.js +6 -1
  350. package/modules/provisioning/tests/provisioning.mjs +8 -8
  351. package/modules/security/secret-scrub.js +40 -4
  352. package/modules/status-ui/public/README.md +1 -1
  353. package/modules/ui-design/docs/design-contract.md +2 -2
  354. package/modules/ui-design/kit/README.md +1 -1
  355. package/modules/ui-design/kit/serve.js +1 -0
  356. package/modules/ui-design/skills/README.md +6 -6
  357. package/modules/ui-design/skills/brandkit/SKILL.md +1 -1
  358. package/modules/ui-design/skills/design-taste-frontend/SKILL.md +1 -1
  359. package/modules/ui-design/skills/design-taste-frontend-v1/SKILL.md +1 -1
  360. package/modules/ui-design/skills/gpt-taste/SKILL.md +1 -1
  361. package/modules/ui-design/skills/high-end-visual-design/SKILL.md +1 -1
  362. package/modules/ui-design/skills/image-to-code/SKILL.md +1 -1
  363. package/modules/ui-design/skills/imagegen-frontend-mobile/SKILL.md +1 -1
  364. package/modules/ui-design/skills/imagegen-frontend-web/SKILL.md +1 -1
  365. package/modules/ui-design/skills/impeccable/SKILL.md +1 -1
  366. package/modules/ui-design/skills/industrial-brutalist-ui/SKILL.md +1 -1
  367. package/modules/ui-design/skills/minimalist-ui/SKILL.md +1 -1
  368. package/modules/ui-design/skills/redesign-existing-projects/SKILL.md +1 -1
  369. package/modules/ui-design/skills/stitch-design-taste/SKILL.md +1 -1
  370. package/modules/ui-design/skills/style/SKILL.md +2 -2
  371. package/modules/ui-design/styles/README.md +3 -3
  372. package/modules/ui-design/styles/blueprint/DESIGN.md +1 -1
  373. package/modules/ui-design/styles/chrome-world/DESIGN.md +1 -1
  374. package/package-lock.json +2 -2
  375. package/package.json +1 -1
  376. package/scripts/gds/adopt-preflight.js +68 -2
  377. package/scripts/gds/audit-rank-roles.js +172 -0
  378. package/scripts/gds/build-cli-package.js +2 -0
  379. package/scripts/gds/claim.js +94 -9
  380. package/scripts/gds/claude-md-template.js +18 -0
  381. package/scripts/gds/device-flow-help.js +72 -0
  382. package/scripts/gds/fitness.js +8 -8
  383. package/scripts/gds/go-live.js +139 -5
  384. package/scripts/gds/init.js +64 -8
  385. package/scripts/gds/land-watch.js +13 -5
  386. package/scripts/gds/login.js +18 -0
  387. package/scripts/gds/mirror-redact.js +13 -2
  388. package/scripts/gds/package-core.js +11 -3
  389. package/scripts/gds/release.js +55 -0
  390. package/scripts/gds/setup.js +5 -39
  391. package/scripts/gds/ship-land.js +10 -2
  392. package/scripts/gds/ship-regen.js +41 -8
  393. package/scripts/gds/start.js +7 -4
  394. package/scripts/gds/worktree-claim-guard.js +27 -1
  395. package/scripts/public-mirror-export.js +17 -4
  396. package/src/bongos/api-errors.js +9 -0
  397. package/src/bongos/artist-gate.js +204 -0
  398. package/src/bongos/repo-info.js +37 -6
  399. package/src/branding.js +15 -0
  400. package/src/module-api.js +41 -1
  401. package/tests/adopt_preflight.mjs +72 -0
  402. package/tests/api_path_404.mjs +28 -0
  403. package/tests/artist_gate.mjs +234 -0
  404. package/tests/artist_gate_release.mjs +283 -0
  405. package/tests/artist_gate_route.mjs +174 -0
  406. package/tests/bongos_login.mjs +69 -2
  407. package/tests/box_scope_predicate.mjs +117 -0
  408. package/tests/claim_error_surface.mjs +163 -1
  409. package/tests/claude_materialize.mjs +9 -0
  410. package/tests/cli_exit_no_abort.mjs +256 -3
  411. package/tests/currency_label.mjs +11 -4
  412. package/tests/discord_approvals.mjs +45 -0
  413. package/tests/go_live.mjs +144 -0
  414. package/tests/init.mjs +52 -2
  415. package/tests/main_worktree_busy_reason.mjs +167 -0
  416. package/tests/membership_kind_lockstep.mjs +88 -0
  417. package/tests/mirror_redact.mjs +14 -0
  418. package/tests/module_api.mjs +7 -0
  419. package/tests/provision_settings_apply.mjs +13 -5
  420. package/tests/provisioning_settings.mjs +11 -7
  421. package/tests/provisioning_settings_apply.mjs +4 -4
  422. package/tests/provisioning_settings_env.mjs +59 -7
  423. package/tests/public_mirror_export.mjs +12 -0
  424. package/tests/rank_roles_audit.mjs +114 -0
  425. package/tests/repo_info_binding.mjs +82 -0
  426. package/tests/scrubber_corpus.mjs +70 -2
  427. package/tests/setup_device_flow_fallback.mjs +16 -0
  428. package/tests/start_rebase_warning.mjs +50 -0
  429. package/tests/task_field_bounds_match_db.mjs +140 -0
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // scripts/gds/device-flow-help.js — the one explanation of a Device-Flow refusal, shared
4
+ // by `bongos setup` and `bongos login` (task 1003400).
5
+ //
6
+ // WHY IT EXISTS AT ALL. GitHub creates an App from a manifest with Device Flow OFF and
7
+ // exposes no API to switch it on — only a checkbox in the App's settings. So on every
8
+ // wizard-born instance the documented terminal sign-in is hard-broken from birth, for
9
+ // every builder, until the owner ticks that box. Nothing in the product can do it for
10
+ // them; the least we can do is say so precisely, and name a route that works meanwhile.
11
+ //
12
+ // WHY IT IS ITS OWN FILE. setup.js grew the detector + the guidance first; login.js threw
13
+ // a bare "could not start sign-in (HTTP 424)" — a dead end sitting directly downstream of
14
+ // the CLI's OWN wrong-instance advice to run `bongos login`. setup.js already requires
15
+ // login.js for runDeviceFlow, so having login.js require setup.js back would close a
16
+ // require cycle. Both now depend on this leaf instead: one source of truth, no cycle.
17
+ //
18
+ // PURE — no fs, no network, no process state; both functions unit-test standalone.
19
+
20
+ // Does this /auth/device/start response carry GitHub's device_flow_disabled refusal?
21
+ //
22
+ // Matches on TEXT, not status, deliberately: the CLI talks to many core versions, and the
23
+ // server envelope moved from 502 to 424 (a 502 body was being eaten by the CDN). Both
24
+ // register. Must NOT match auth_not_configured or any other upstream failure — those have
25
+ // different cures, and a wrong cure is worse than a generic error.
26
+ function isDeviceFlowDisabled(start) {
27
+ if (!start || start.ok) return false;
28
+ const err = start.data && start.data.error;
29
+ const text = [
30
+ typeof err === 'string' ? err : '',
31
+ err && typeof err === 'object' ? `${err.code || ''} ${err.message || ''}` : '',
32
+ start.data && start.data.message ? String(start.data.message) : '',
33
+ ].join(' ');
34
+ return /device_flow_disabled/i.test(text);
35
+ }
36
+
37
+ // The plain-language guidance: what happened, the web route that works RIGHT NOW, and the
38
+ // owner's one-checkbox repair. Returns an array of lines (the callers join them).
39
+ //
40
+ // `finish` names the caller, because only the closing move differs: setup prompts for the
41
+ // token inline ("paste it below"), login has no such prompt and must tell you to run the
42
+ // one-liner instead. Everything above that step is genuinely shared and stays shared.
43
+ function deviceFlowFallbackLines(base, { finish = 'setup' } = {}) {
44
+ const settings = `${base}/builders/settings`;
45
+ const close = finish === 'login'
46
+ ? [
47
+ ' 3. Copy the one-liner it shows and run it in this shell — that writes',
48
+ ' the same session `bongos login` would have.',
49
+ ]
50
+ : [
51
+ ' 3. Copy the token it shows and paste it below (or run the printed',
52
+ ' one-liner in another shell, then re-run setup).',
53
+ ];
54
+ return [
55
+ '',
56
+ "This project's GitHub App has Device Flow switched off, so terminal sign-in",
57
+ 'cannot start. Apps created by the one-click setup begin this way — GitHub',
58
+ 'only offers the switch as a checkbox in the App settings; no setup flow can',
59
+ 'tick it automatically.',
60
+ '',
61
+ 'Sign in through the web instead (works right now):',
62
+ ` 1. Open ${settings} and sign in with GitHub.`,
63
+ ' 2. Under "CLI Access", click "Re-issue CLI token".',
64
+ ...close,
65
+ '',
66
+ 'To repair terminal sign-in for everyone (the project owner, once):',
67
+ ' GitHub → Settings → Developer settings → GitHub Apps → your app →',
68
+ ' check "Enable Device Flow" → Save. Web sign-in is unaffected either way.',
69
+ ];
70
+ }
71
+
72
+ module.exports = { isDeviceFlowDisabled, deviceFlowFallbackLines };
@@ -520,17 +520,17 @@ const KERNEL_FILES = [
520
520
  // KERNEL (already in the module-scope-map kernel key) and imports only the
521
521
  // kernel (instance-config) + node built-ins, so it joins the roster cleanly.
522
522
  'src/bongos/repo-info.js',
523
- // task 1002331 (ADR 0182 D6 / ADR 0247): the project's composed join/apply door —
524
- // a deterministic classifier over three policy values, the same category as
523
+ // task 1002331 (ADR 0182 D6 / ADR 0247): the project's composed join/apply door — a
524
+ // deterministic classifier over three policy values, the same category as
525
525
  // route-rank-check.js and goal-scope-check.js above (a trust decision the doorway
526
526
  // hands to modules so no module re-derives it). The doorway re-exports it, so
527
527
  // leaving it off this roster reads as a kernel->non-kernel leak; it imports only
528
- // branding.js, instance-config.js and logger.js — all on this roster — so
529
- // joining costs nothing and buys the import-direction guarantee on the file that
530
- // decides who may knock. (Task 1003579 removed its require of auth-admission.js
531
- // and moved joinabilityMode the other way: the gate now reads the composed door,
532
- // so the reader had to sit with the composer or the two files would have cycled.)
533
- 'src/bongos/project-door.js',
528
+ // branding.js, instance-config.js and logger.js — all on this roster — so joining
529
+ // costs nothing and buys the import-direction guarantee on the file that decides
530
+ // who may knock. (Task 1003579 moved joinabilityMode the other way: the gate reads
531
+ // the composed door, so reader and composer had to sit together or the two would
532
+ // cycle.) The ARTIST GATE joins for the same reason task 1003575, ADR 0241.
533
+ 'src/bongos/project-door.js', 'src/bongos/artist-gate.js',
534
534
  ];
535
535
 
536
536
  // Kernel files exempt from the no-module-keys scan (Check 10) because naming a
@@ -44,7 +44,7 @@
44
44
  const fs = require('node:fs');
45
45
  const path = require('node:path');
46
46
  const { spawnSync } = require('node:child_process');
47
- const { arg, hasFlag } = require('./cli-lib');
47
+ const { arg, hasFlag, apiCall } = require('./cli-lib');
48
48
  const divergence = require('./deploy-divergence.js');
49
49
 
50
50
  const ROOT = path.resolve(__dirname, '..', '..');
@@ -370,6 +370,57 @@ function refreshMain({ run = spawnSync } = {}) {
370
370
  return { ok: true };
371
371
  }
372
372
 
373
+ // Ask the LIVE instance about its own artist gate (ADR 0241, task 1003575).
374
+ //
375
+ // The instance answers because it is the only party that can: the policy arrives
376
+ // in its web.env and its board holds the reviews. This script only decides what to
377
+ // do with the answer, and the refusal sentence is composed there rather than here
378
+ // so one wording serves every caller.
379
+ //
380
+ // NEVER THROWS. Every failure — no session, a 401, an instance too old to carry
381
+ // the route, a network error — resolves to { unread: <why> }, which renders as a
382
+ // note. See artistGateEntries for why an unread gate is not a halt.
383
+ // The FULL path, not a bare `/artist-gate`. cli-lib's apiCall composes
384
+ // `${base}${urlPath}` verbatim and `base` is the instance ORIGIN — every other
385
+ // caller in this repo spells `/api/bongos/…` by hand for the same reason. Getting
386
+ // this wrong is silent in the worst way: the request lands on the origin's
387
+ // catch-all, the gate reports itself unread on every instance including the ones
388
+ // that fully support it, and the deploy check never fires. Exported so a test can
389
+ // pin it against the route's REAL mount point in the generated OpenAPI spec.
390
+ const ARTIST_GATE_PATH = '/api/bongos/artist-gate';
391
+ // The levels a real answer can carry — mirrored from src/bongos/artist-gate.js
392
+ // rather than imported, because this script also runs from an INSTALLED core on a
393
+ // box where that path is inside the package, and a require of it here would make
394
+ // the deploy tool depend on the server's own tree. Pinned by the same test that
395
+ // pins the path.
396
+ const ARTIST_GATE_LEVELS = ['off', 'advisory', 'strict'];
397
+
398
+ async function readArtistGate(call = apiCall) {
399
+ try {
400
+ const res = await call('GET', ARTIST_GATE_PATH, null, { allowUnauthenticated: true, allowBoxScope: true });
401
+ if (res.status === 404) return { unread: `this instance does not serve ${ARTIST_GATE_PATH} — it predates ADR 0241` };
402
+ // A 5xx is a gate that EXISTS and BROKE, which is not the same class of
403
+ // unknown as "no session" or "too old to have the route". The gate is
404
+ // fail-closed by design, so the one case where we know it is there and
405
+ // cannot get an answer from it HALTS rather than degrading to a note —
406
+ // otherwise a broken read is a quieter way past the gate than the owner's
407
+ // own override, which is the asymmetry worth refusing.
408
+ if (res.status >= 500) return { broken: `GET ${ARTIST_GATE_PATH} returned ${res.status}` };
409
+ if (!res.ok) return { unread: `GET ${ARTIST_GATE_PATH} returned ${res.status}` };
410
+ const d = res.data || {};
411
+ // A 200 that is not this route's answer — an SPA index.html served by a
412
+ // catch-all, a proxy's own page — must read as UNREAD, never as a gate that
413
+ // holds nothing. "I got a 200 from something" is the one failure that would
414
+ // wave a deploy through in silence, which is worse than any halt.
415
+ if (!ARTIST_GATE_LEVELS.includes(d.mode)) {
416
+ return { unread: `GET ${ARTIST_GATE_PATH} answered ${res.status} but not with a gate — got mode ${JSON.stringify(d.mode)}` };
417
+ }
418
+ return { mode: d.mode, since: d.since || null, holds: !!d.holds, reviews: d.reviews || [], halt: d.halt || null };
419
+ } catch (e) {
420
+ return { unread: (e && e.message) || 'request failed' };
421
+ }
422
+ }
423
+
373
424
  // Parse a `key=value` marker into its parts. Kept pure + exported: the marker is
374
425
  // the answer to the trap that the release current when your fix merges usually
375
426
  // does NOT contain your fix (ADR 0161 carriers bump on their own cadence).
@@ -394,12 +445,68 @@ const HALTS = {
394
445
  MARKER_UNCHECKED: 'could not inspect the target version — the content proof did not run',
395
446
  BOX_TREE_DIRTY: 'the instance repo on the box has uncommitted changes',
396
447
  LIVE_AHEAD: 'live is AHEAD of the target',
448
+ ARTIST_REVIEW_UNRESOLVED: 'an unresolved artist review holds this deploy (ADR 0241)',
449
+ ARTIST_GATE_UNREADABLE: 'the artist gate is there and answering with an error — this deploy cannot tell whether a review holds it',
397
450
  };
398
451
 
399
- function planGoLive({ div, target, targetVersion, dirtyCheckout, allowUnreleased = false }) {
452
+ // The artist gate (ADR 0241, task 1003575). This is the ONE place in the deploy
453
+ // path that the artist's authority touches, and the boundary is the whole point:
454
+ // `strict` may delay a PIN MOVE and nothing else. Nothing in the ship, grade,
455
+ // merge or land path consults this — a builder's work is credited and on `main`
456
+ // long before a go-live runs, which is what lets this gate coexist with ADR 0162
457
+ // instead of recreating the strand it retired.
458
+ //
459
+ // It reads a STATE, not a verdict: the instance reports which artist review tasks
460
+ // are still open, never whether an artist approved anything. See
461
+ // src/bongos/artist-gate.js, which composes the refusal sentence this renders.
462
+ // The halt and/or note one gate read contributes. Shared by planGoLive and by the
463
+ // gitless (on-box, no checkout) path, so a lane that skips the git-derived checks
464
+ // does not also silently skip the gate — the deploy is the same deploy either way.
465
+ // PURE.
466
+ function artistGateEntries(gate) {
467
+ const halts = [];
468
+ const notes = [];
469
+ if (gate && gate.halt) {
470
+ halts.push({ code: 'ARTIST_REVIEW_UNRESOLVED', message: gate.halt.message, detail: gate.halt.detail });
471
+ }
472
+ // A gate that is THERE and BROKEN halts. This is the fail-closed half: we know
473
+ // the instance has the route, so "it 500s" is a gate we cannot get an answer
474
+ // from, not a gate that does not apply. Its remedy is the instance's own logs,
475
+ // and it is named as such rather than as an artist problem.
476
+ if (gate && gate.broken) {
477
+ halts.push({
478
+ code: 'ARTIST_GATE_UNREADABLE',
479
+ message: HALTS.ARTIST_GATE_UNREADABLE,
480
+ detail: `${gate.broken}. The route exists, so this is a fault on the instance, not a missing feature — read its logs for the GET /artist-gate failure. Deploying past it would mean moving the pin without knowing whether a review holds it.`,
481
+ });
482
+ }
483
+ // NOT KNOWING is a different thing again, and it must not render as "nothing
484
+ // holds". It is a note rather than a halt on purpose: a go-live is already
485
+ // deliberate and reversible, and halting every deploy because there is no CLI
486
+ // session — or because the instance predates the route entirely — would brick
487
+ // the pin with no task id to release against, which is the blocked-state-that-
488
+ // does-not-explain-itself failure ADR 0241 §4 names.
489
+ if (gate && gate.unread) {
490
+ notes.push({
491
+ code: 'ARTIST_GATE_UNREAD',
492
+ message: `could not read this instance's artist gate (${gate.unread}) — if the project runs artist_gate 'strict', an unresolved artist review may be holding this deploy and this plan cannot see it`,
493
+ });
494
+ }
495
+ return { halts, notes };
496
+ }
497
+
498
+ function planGoLive({ div, target, targetVersion, dirtyCheckout, allowUnreleased = false, artistGate = null }) {
400
499
  const halts = [];
401
500
  const notes = [];
402
501
 
502
+ // Evaluated FIRST, before the git-derived checks, because it is the halt whose
503
+ // remedy takes longest: a review someone has to do is not a thing you fix in the
504
+ // next ten seconds, and finding it out after the other five checks passed is
505
+ // strictly worse than finding it out now.
506
+ const gate = artistGateEntries(artistGate);
507
+ halts.push(...gate.halts);
508
+ notes.push(...gate.notes);
509
+
403
510
  if (dirtyCheckout) halts.push({ code: 'DIRTY_CHECKOUT', message: HALTS.DIRTY_CHECKOUT, detail: dirtyCheckout });
404
511
 
405
512
  if (!div || div.undetermined) {
@@ -446,6 +553,13 @@ function renderPlan(p, { targetName, configPath } = {}) {
446
553
  L.push(` target ${p.targetVersion || '(unresolved)'}`);
447
554
  if (p.marker) L.push(` verify ${p.marker.path} contains "${p.marker.needle}"`);
448
555
  if (p.registry) L.push(` registry ${p.registry.ok ? `carries ${p.targetVersion}` : 'MISSING'}`);
556
+ // The gate's state is worth a line even when it holds nothing: "artist gate
557
+ // advisory" is the answer to "why did nothing stop?", and a reader who cannot
558
+ // see the level has to go and find it.
559
+ if (p.artistGate && p.artistGate.mode) {
560
+ const open = (p.artistGate.reviews || []).length;
561
+ L.push(` artist gate ${p.artistGate.mode}${open ? ` · ${open} unresolved review${open === 1 ? '' : 's'}` : ' · nothing open'}`);
562
+ }
449
563
  L.push('');
450
564
  for (const n of p.notes || []) L.push(` ⚠ ${n.code} — ${n.message}`);
451
565
  if (!p.halts.length) {
@@ -790,9 +904,28 @@ async function runGoLive({ target, targetName, configPath, json, apply, allowUnr
790
904
  const dirtyRes = gitless ? { status: 1 } : spawnSync('git', ['status', '--porcelain'], { cwd: ROOT, encoding: 'utf8' });
791
905
  const dirtyCheckout = dirtyRes.status === 0 && (dirtyRes.stdout || '').trim() ? (dirtyRes.stdout || '').trim().split('\n').slice(0, 5).join(', ') : null;
792
906
 
907
+ // Always read, and deliberately NOT skippable by a flag. ADR 0241 §3 names one
908
+ // escape hatch — the project OWNER's override-request, rationale required and
909
+ // audit-logged — and a `--skip` here would be a second authority path that any
910
+ // builder with the shell could take silently, which is exactly the shape that
911
+ // ADR rejects. An unread gate already degrades to a note; that is the only
912
+ // by-pass, and it says so on screen.
913
+ const artistGate = await readArtistGate();
914
+
915
+ const gitlessGate = artistGateEntries(artistGate);
793
916
  const plan = gitless
794
- ? { halts: [], notes: [{ code: 'NO_CORE_CHECKOUT', message: `no core git checkout here — target ${targetVersion} was given explicitly, so the git-derived checks (unreleased-on-main, dirty tree) are skipped` }], from: null, targetVersion, target }
795
- : planGoLive({ div, target, targetVersion, dirtyCheckout, allowUnreleased });
917
+ ? {
918
+ halts: gitlessGate.halts,
919
+ notes: [
920
+ { code: 'NO_CORE_CHECKOUT', message: `no core git checkout here — target ${targetVersion} was given explicitly, so the git-derived checks (unreleased-on-main, dirty tree) are skipped` },
921
+ ...gitlessGate.notes,
922
+ ],
923
+ from: null,
924
+ targetVersion,
925
+ target,
926
+ }
927
+ : planGoLive({ div, target, targetVersion, dirtyCheckout, allowUnreleased, artistGate });
928
+ plan.artistGate = artistGate;
796
929
  plan.marker = marker;
797
930
  if (!fetched.ok && !fetched.skipped) {
798
931
  // Say so rather than silently planning off a ref of unknown age.
@@ -860,7 +993,8 @@ if (require.main === module) {
860
993
  }
861
994
 
862
995
  module.exports = {
863
- loadConfig, selectTarget, validateTarget, planGoLive, parseMarker, parseMarkerHits,
996
+ loadConfig, selectTarget, validateTarget, planGoLive, artistGateEntries, readArtistGate, ARTIST_GATE_PATH, ARTIST_GATE_LEVELS,
997
+ parseMarker, parseMarkerHits,
864
998
  renderPlan, renderReport, failureExcerpt, asOwner, isSafeToken, NPM_AUTH_PREAMBLE, refreshMain, isLocalTarget, gitRefExists, SUDO_PREAMBLE,
865
999
  applyGoLive, upgradeCommand, pinStep, pinModeOf,
866
1000
  CONFIG_TEMPLATE, HALTS, SEMVER_RE, PIN_MODES, DEFAULT_PIN_MODE,
@@ -525,6 +525,62 @@ function buildReadme(branding = {}) {
525
525
  ].join('\n');
526
526
  }
527
527
 
528
+ // The START-HERE content as an APPENDABLE section, for a repo that already has a
529
+ // README of its own. Fenced by a stable marker so re-running init is idempotent and so
530
+ // an owner can find (and delete) the block we added.
531
+ //
532
+ // WHY this is not just buildReadme: an ADOPTED repo always has a README, and the
533
+ // write-if-absent rule below therefore skipped the first-run guide on every single adopt
534
+ // — silently, logging "already present — left untouched" as though nothing were owed.
535
+ // That removed the one human-facing instruction to run `npm install`, without which the
536
+ // repo has no `bongos` command at all. Appending mirrors how the adopt path already
537
+ // treats .gitignore: keep what the owner wrote, add only what the instance needs.
538
+ const README_MARKER = '<!-- cloud-bongos:start-here -->';
539
+
540
+ function buildReadmeSection(branding = {}) {
541
+ const guide = buildReadme(branding);
542
+ // Reuse the canonical guide, demoted one heading level so it nests under the owner's
543
+ // own README, and drop its H1 title (their README already has one).
544
+ const body = guide
545
+ .split('\n')
546
+ .slice(1) // drop "# <product>"
547
+ .join('\n')
548
+ .replace(/^## /gm, '### ')
549
+ .trim();
550
+ return `${README_MARKER}\n\n## Working on this project (Cloud Bongos)\n\n${body}\n`;
551
+ }
552
+
553
+ // Append the START-HERE section to an existing README unless it is already there.
554
+ // PURE (text in → text out) so the idempotence is unit-testable without fs.
555
+ // Returns { text, action } where action is 'appended' | 'present'.
556
+ function appendReadmeSection(existing, section) {
557
+ const prev = String(existing == null ? '' : existing);
558
+ if (prev.includes(README_MARKER)) return { text: prev, action: 'present' };
559
+ return { text: `${prev.replace(/\n*$/, '\n')}\n---\n\n${section}`, action: 'appended' };
560
+ }
561
+
562
+ // Put the first-run guide in the repo's README whichever state it is in: mint the whole
563
+ // guide when there is no README, else append the marked section. Shared by the greenfield
564
+ // and adopt paths so neither can silently owe it. Returns the action taken.
565
+ function layerReadme(branding, { dir, dryRun, log = console.log } = {}) {
566
+ const readmePath = path.join(dir, 'README.md');
567
+ if (!fs.existsSync(readmePath)) {
568
+ if (dryRun) { log(` [dry-run] would write README.md (first-run START-HERE)`); return 'created'; }
569
+ fs.writeFileSync(readmePath, buildReadme(branding));
570
+ log(` ✓ wrote README.md (first-run START-HERE)`);
571
+ return 'created';
572
+ }
573
+ const { text, action } = appendReadmeSection(fs.readFileSync(readmePath, 'utf8'), buildReadmeSection(branding));
574
+ if (action === 'present') {
575
+ log(' • README.md already carries the Cloud Bongos start-here section — left untouched.');
576
+ return 'present';
577
+ }
578
+ if (dryRun) { log(' [dry-run] would append the Cloud Bongos start-here section to the existing README.md'); return 'appended'; }
579
+ fs.writeFileSync(readmePath, text);
580
+ log(' ✓ appended the Cloud Bongos start-here section to README.md (your own content is untouched)');
581
+ return 'appended';
582
+ }
583
+
528
584
  function writeConfigs(spec, { dir, force, dryRun, coreDep, log = console.log }) {
529
585
  const configDir = path.join(dir, 'config');
530
586
  const brandingPath = path.join(configDir, 'branding.json');
@@ -572,14 +628,9 @@ function writeConfigs(spec, { dir, force, dryRun, coreDep, log = console.log })
572
628
  log(` ✓ wrote ${path.relative(dir, modulesPath)}`);
573
629
  log(` ✓ wrote ${path.relative(dir, hierarchyPath)}`);
574
630
  log(` ✓ wrote ${path.relative(dir, pkgPath)} (pins ${CORE_PKG} → ${pinNote})`);
575
- // README START-HERE (task 1002404): write-if-absent so a --force re-init or an
576
- // owner's own README is never clobbered.
577
- if (!fs.existsSync(readmePath)) {
578
- fs.writeFileSync(readmePath, buildReadme(branding));
579
- log(` ✓ wrote ${path.relative(dir, readmePath)} (first-run START-HERE)`);
580
- } else {
581
- log(` • ${path.relative(dir, readmePath)} already present — left untouched`);
582
- }
631
+ // README START-HERE (task 1002404): mint it when absent, else APPEND the marked
632
+ // section. Never a blind overwrite — an owner's own README content always survives.
633
+ layerReadme(branding, { dir, dryRun: false, log });
583
634
  return { brandingPath, modulesPath, hierarchyPath, pkgPath, readmePath, wrote: true };
584
635
  }
585
636
 
@@ -816,6 +867,10 @@ async function runAdopt(spec, { dir, dryRun, noSeed = false, accept = false, cor
816
867
 
817
868
  const cfg = layerConfigs(spec, { dir, dryRun, log });
818
869
  const pkg = layerPackageJson(spec, { dir, dryRun, log, ...(coreDep ? { coreRange: coreDep } : {}) });
870
+ // The first-run guide, which adopt used to skip entirely: an adopted repo always has a
871
+ // README, so the greenfield write-if-absent rule never fired and the one instruction to
872
+ // run `npm install` was silently owed on every adopt.
873
+ layerReadme(buildBrandingConfig(spec), { dir, dryRun, log });
819
874
 
820
875
  // Vendor the pinned core tarball + generate the lockfile, mirroring the greenfield
821
876
  // --vendor-core steps, so the adopted repo is `npm ci --omit=dev`-installable (ADR 0108).
@@ -1061,6 +1116,7 @@ module.exports = {
1061
1116
  provisioningSlug, provisioningRequest, productionTopologyKickoff,
1062
1117
  // --adopt (brownfield layering, ADR 0121 / task 2042)
1063
1118
  mergeCoreDependency, coreDepRange, layerConfigs, layerPackageJson, runAdopt,
1119
+ buildReadmeSection, appendReadmeSection, layerReadme, README_MARKER,
1064
1120
  KNOWN_MODULES, HOSTING_SHAPES, CORE_PKG,
1065
1121
  // greenfield installability (ADR 0108 gap 1/4, task 2053)
1066
1122
  buildInstancePackageJson, coreVersionSafe, INSTANCE_START_SCRIPT, INSTANCE_MIGRATE_SCRIPT,
@@ -21,7 +21,7 @@
21
21
 
22
22
  const fs = require('node:fs');
23
23
  const path = require('node:path');
24
- const { SESSION_PATH, cliClient } = require('./cli-lib');
24
+ const { SESSION_PATH, cliClient, cliExit } = require('./cli-lib');
25
25
  const shipCard = require('../../modules/lifecycle/ship-card');
26
26
 
27
27
  // Breadcrumbs live beside the session token (per-machine, per-instance config dir —
@@ -174,14 +174,22 @@ module.exports = {
174
174
  if (require.main === module) {
175
175
  const hookMode = process.argv.includes('--hook');
176
176
  (async () => {
177
+ // task 1002592: every exit below routes through cliExit (task 1002734's rule).
178
+ // This script exits 0 on ALL paths on purpose — it is hook-invoked and must
179
+ // never report failure — but on Windows the post-request teardown aborted with
180
+ // "Assertion failed: !(handle->flags & UV_HANDLE_CLOSING)" and handed the hook
181
+ // 127, which conventionally reads as "command not found". Measured 3/3 before
182
+ // this change: the one script written never to fail always reported failure.
183
+ // Draining also lets the --hook stdout write flush, which a hard exit can cut
184
+ // mid-JSON — the card hook parses that stdout.
177
185
  // Cheap idle fast-path: no breadcrumbs → do nothing (no network).
178
- if (readBreadcrumbs().length === 0) { if (!hookMode) console.error('land-watch: no pending lands.'); process.exit(0); }
186
+ if (readBreadcrumbs().length === 0) { if (!hookMode) console.error('land-watch: no pending lands.'); return cliExit(0); }
179
187
  const envelope = await watch();
180
188
  if (hookMode) {
181
189
  if (envelope) process.stdout.write(JSON.stringify(envelope));
182
- process.exit(0);
190
+ return cliExit(0);
183
191
  }
184
192
  console.error(envelope ? `land-watch: a task landed — shipped card ready (${envelope.title}).` : 'land-watch: nothing landed this pass.');
185
- process.exit(0);
186
- })().catch(() => process.exit(0)); // never throw from a hook-invoked script
193
+ return cliExit(0);
194
+ })().catch(() => cliExit(0)); // never throw from a hook-invoked script
187
195
  }
@@ -23,6 +23,10 @@
23
23
  const readline = require('node:readline');
24
24
  const { spawnSync } = require('node:child_process');
25
25
  const { saveSession, loadStoredSession, listStoredSessions } = require('./cli-lib');
26
+ // The device-flow refusal detector + its plain-language cure (task 1003400). A leaf
27
+ // module, NOT setup.js: setup requires this file for runDeviceFlow, so requiring it
28
+ // back would close a require cycle. Both callers depend on the leaf instead.
29
+ const { isDeviceFlowDisabled, deviceFlowFallbackLines } = require('./device-flow-help');
26
30
  const { branding } = require('../../src/branding');
27
31
 
28
32
  // --- pure helpers (unit-tested) ---------------------------------------------
@@ -102,6 +106,20 @@ async function runDeviceFlow(base, { log, idp = null }) {
102
106
  if (start.status === 503 || (start.data && (start.data.error === 'auth_not_configured' || (start.data.error && start.data.error.code === 'idp_signing_unconfigured')))) {
103
107
  throw new Error('that instance has no sign-in configured yet — ask its Archon to set it up.');
104
108
  }
109
+ // Device Flow is OFF on every GitHub App born from a manifest, and GitHub exposes no
110
+ // API to enable it — so terminal sign-in is broken from birth on a fresh instance,
111
+ // for everyone, until its owner ticks one checkbox. This threw a bare
112
+ // "could not start sign-in (HTTP 424)", which is where the CLI's OWN wrong-instance
113
+ // refusal sends every multi-project user: a dead end at the end of our own advice.
114
+ // In federated mode the device flow runs against the HUB, so the cure is on the hub's
115
+ // app — point the guidance at whichever origin actually refused.
116
+ if (isDeviceFlowDisabled(start)) {
117
+ const refusedBy = idp ? idp.origin : base;
118
+ throw new Error([
119
+ 'GitHub refused the device-code request for this project.',
120
+ ...deviceFlowFallbackLines(refusedBy, { finish: 'login' }),
121
+ ].join('\n'));
122
+ }
105
123
  if (!start.ok || !start.data || !start.data.device_code) {
106
124
  throw new Error(`could not start sign-in (HTTP ${start.status}).`);
107
125
  }
@@ -286,9 +286,20 @@ function findLeaks(text, sensitiveLiterals) {
286
286
  // findSecretLeak — true if the generic secret scrubber WOULD change this text, i.e. a
287
287
  // secret shape is present. The export gate runs this in addition to findLeaks so a
288
288
  // stray credential in an old commit fails the publish closed rather than leaking.
289
- function findSecretLeak(text, extraSecrets = []) {
289
+ //
290
+ // `opts` MUST be whatever the scrub being audited was given (task 1003873). Unlike
291
+ // findLeaks — which is deliberately STRICTER than redaction, and independent of it by
292
+ // design — this one is DIFFERENTIAL: it re-scrubs and asks "did anything change?". An
293
+ // option the audit does not share therefore makes it disagree with its own subject by
294
+ // construction, and reads that disagreement as a leak. That is not a safety margin, it
295
+ // is a false positive: task 1002460's knownDocNames allowlist spares a long ADR
296
+ // filename from the entropy sweep, an audit without the allowlist re-redacts it, the
297
+ // text differs, and EVERY published doc reports a phantom secret shape. Because the
298
+ // gate is fail-closed the packager then produces nothing — it took the publish lane
299
+ // down for seven consecutive core releases before anyone read past the symptom.
300
+ function findSecretLeak(text, extraSecrets = [], opts = undefined) {
290
301
  if (typeof text !== 'string' || text.length === 0) return false;
291
- return scrubSecrets(text, extraSecrets) !== text;
302
+ return scrubSecrets(text, extraSecrets, opts) !== text;
292
303
  }
293
304
 
294
305
  module.exports = {
@@ -193,7 +193,7 @@ function functionalSecretLeaks(view, envSecrets) {
193
193
  // * FUNCTIONAL: VERBATIM buffer. `leaks` = exact live-credential matches only (FATAL).
194
194
  // `advisories` = brand-identity substrings + public IPs that ship as-is (non-fatal, for
195
195
  // human awareness) — REUSES findLeaks so the detector is defined once.
196
- function processCoreFile({ pathRel, raw, rules, sensitiveLiterals, envSecrets }) {
196
+ function processCoreFile({ pathRel, raw, rules, sensitiveLiterals, envSecrets, knownDocNames }) {
197
197
  // An auto-loaded agent doc (CLAUDE.md/AGENTS.md) → ship a brand-free stub, never the
198
198
  // core's own project memory (task 2121). The stub is brand-free by construction, so it
199
199
  // needs no redaction and can never leak — it short-circuits ahead of the doc path.
@@ -201,7 +201,7 @@ function processCoreFile({ pathRel, raw, rules, sensitiveLiterals, envSecrets })
201
201
  return { buf: Buffer.from(CORE_AGENT_DOC_STUB, 'utf8'), leaks: [], advisories: [], kind: 'stub' };
202
202
  }
203
203
  if (isDocPath(pathRel)) {
204
- const r = redactBlob({ buf: raw, rules, sensitiveLiterals, envSecrets });
204
+ const r = redactBlob({ buf: raw, rules, sensitiveLiterals, envSecrets, knownDocNames });
205
205
  const leaks = r.leaks.map((l) => ({ label: `doc:${l.label}`, sample: l.sample }));
206
206
  if (r.secret) leaks.push({ label: 'doc:secret-shape' });
207
207
  return { buf: r.buf, leaks, advisories: [], kind: 'doc' };
@@ -817,6 +817,14 @@ function main() {
817
817
  // 4. Process every file IN MEMORY (processCoreFile: docs redacted, functional verbatim)
818
818
  // + the fail-closed no-leak gate. Nothing touches dist/ until the ENTIRE set is proven
819
819
  // clean (atomic abort). Brand/topology in functional files are non-fatal ADVISORIES.
820
+ // task 1002460 — the doc-link allowlist. The entropy sweep cannot tell a long
821
+ // ADR filename from a secret by shape, so it collapsed every one of them to
822
+ // <redacted>.md on the way out. This is the set of names actually being
823
+ // published, so a name in it is a FILE and not a guess; anything else, secrets
824
+ // included, is redacted exactly as before.
825
+ const knownDocNames = new Set(
826
+ publishable.map((e) => e.path.split('/').pop()).filter((n) => /\.(md|sql)$/i.test(n)),
827
+ );
820
828
  const staged = []; // { name, mode, buf } (name includes TAR_ROOT)
821
829
  const filesMeta = []; // { path, mode, sha256 } (source files only — the pin domain)
822
830
  const leaks = [];
@@ -829,7 +837,7 @@ function main() {
829
837
  try { raw = gitBuf(['cat-file', 'blob', e.sha]); }
830
838
  catch (err) { leaks.push({ path: e.path, label: `read-failed:${err.message}` }); continue; }
831
839
  const { buf, leaks: fileLeaks, advisories: fileAdvisories, kind } =
832
- processCoreFile({ pathRel: e.path, raw, rules, sensitiveLiterals, envSecrets });
840
+ processCoreFile({ pathRel: e.path, raw, rules, sensitiveLiterals, envSecrets, knownDocNames });
833
841
  for (const l of fileLeaks) leaks.push({ path: e.path, label: l.label, sample: l.sample });
834
842
  for (const a of fileAdvisories) advisories.push({ path: e.path, label: a.label, sample: a.sample });
835
843
  if (kind === 'doc') docCount++; else if (kind === 'stub') stubCount++; else funcCount++;
@@ -6,7 +6,46 @@
6
6
  //
7
7
  // The task returns to 'ready' status and is claimable by another session.
8
8
 
9
+ const { execFileSync } = require('node:child_process');
10
+ const path = require('node:path');
9
11
  const { cliClient, requireSession, arg } = require('./cli-lib');
12
+ const { parkedMainCheckoutWarning } = require('./worktree-claim-guard');
13
+
14
+ // gitAt(args, cwd) — run git with an ARGV ARRAY and no shell.
15
+ //
16
+ // Deliberately not `execSync('git -C "<path>" …')`. A resolved filesystem path is
17
+ // not trusted input: a directory name containing a quote, backtick or `$()` would
18
+ // break out of the quoting and execute under cmd.exe/sh. execFileSync never
19
+ // spawns a shell, so the path cannot be interpreted as anything but a path — a
20
+ // stronger guarantee than the `{ cwd }` form, which still shells the command
21
+ // string. (Found by the grader on this task, and it was right: the first cut
22
+ // interpolated the path into a shell string.)
23
+ //
24
+ // Kept local rather than imported from ship-git.js on purpose: release.js ships
25
+ // in the PUBLIC client-only @cloudbongos/cli package, and nothing in the ship-*
26
+ // family is in that manifest. tests/cli_package.mjs installs the tarball and RUNS
27
+ // every verb, so an import from outside it is a crash at require time, not a
28
+ // lint-level nit — that is exactly how this file broke once already.
29
+ function gitAt(args, cwd) {
30
+ try {
31
+ return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
32
+ } catch (_) {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ // The MAIN checkout, not this worktree — from a linked worktree they differ, and
38
+ // the parked-tree warning is only ever about the main one.
39
+ //
40
+ // Resolved with git rather than by importing ship-state's REPO_ROOT for the same
41
+ // packaging reason: ship-state pulls in modules/lifecycle/ship-preflight, which
42
+ // would drag server-side lifecycle code into a client-only package.
43
+ // `--git-common-dir` is the shared .git of the whole worktree family, so its
44
+ // parent is the main checkout from anywhere.
45
+ function mainCheckoutRoot() {
46
+ const common = gitAt(['rev-parse', '--path-format=absolute', '--git-common-dir'], process.cwd());
47
+ return common ? path.dirname(common) : null;
48
+ }
10
49
 
11
50
 
12
51
  (async () => {
@@ -65,6 +104,22 @@ const { cliClient, requireSession, arg } = require('./cli-lib');
65
104
  }
66
105
  console.log(`Released task #${r.data.taskId}: ${r.data.taskTitle}`);
67
106
  console.log(`Status returned to 'ready'. No credits awarded.`);
107
+ // task 1002871: a claim worked under the `--here` hatch leaves the MAIN
108
+ // checkout on the feature branch, and giving the claim up is exactly when that
109
+ // tree stops being watched. Warn, never restore — the branch may hold real
110
+ // uncommitted work, and release.js cannot tell that from debris. Strictly
111
+ // best-effort: a diagnostic must never fail a release that already succeeded.
112
+ try {
113
+ const root = mainCheckoutRoot();
114
+ const branch = root && gitAt(['rev-parse', '--abbrev-ref', 'HEAD'], root);
115
+ // gitAt already trims, so a non-empty string IS dirt.
116
+ const dirty = root && gitAt(['status', '--porcelain'], root);
117
+ const lines = parkedMainCheckoutWarning({ branch, dirty: !!dirty });
118
+ if (lines.length) {
119
+ console.log('');
120
+ for (const line of lines) console.log(line);
121
+ }
122
+ } catch (_) { /* never let a warning break a successful release */ }
68
123
  })().catch((err) => {
69
124
  console.error('fatal:', err);
70
125
  process.exit(1);