@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.
- package/.bongos-core.json +5727 -0
- package/.claude/skills/blocker-review/SKILL.md +91 -0
- package/.claude/skills/blocker-solve/SKILL.md +96 -0
- package/.claude/skills/builder-backup/SKILL.md +54 -0
- package/.claude/skills/builder-box/SKILL.md +45 -0
- package/.claude/skills/builder-claim/SKILL.md +76 -0
- package/.claude/skills/builder-connect/SKILL.md +59 -0
- package/.claude/skills/builder-cost/SKILL.md +47 -0
- package/.claude/skills/builder-end/SKILL.md +81 -0
- package/.claude/skills/builder-exit/SKILL.md +88 -0
- package/.claude/skills/builder-key/SKILL.md +40 -0
- package/.claude/skills/builder-reauth/SKILL.md +50 -0
- package/.claude/skills/builder-redteam/SKILL.md +109 -0
- package/.claude/skills/builder-release/SKILL.md +41 -0
- package/.claude/skills/builder-setup/SKILL.md +62 -0
- package/.claude/skills/builder-ship/SKILL.md +79 -0
- package/.claude/skills/builder-stage/SKILL.md +51 -0
- package/.claude/skills/builder-start/SKILL.md +46 -0
- package/.claude/skills/close-box/SKILL.md +43 -0
- package/.claude/skills/design-sync/SKILL.md +69 -0
- package/.claude/skills/dev/SKILL.md +49 -0
- package/.claude/skills/feedback/SKILL.md +32 -0
- package/.claude/skills/figma-design-sync/SKILL.md +74 -0
- package/.claude/skills/goal-review/SKILL.md +89 -0
- package/.claude/skills/idea-triage/SKILL.md +144 -0
- package/.claude/skills/ideate/SKILL.md +82 -0
- package/.claude/skills/merge-mode/SKILL.md +169 -0
- package/.claude/skills/new-project/SKILL.md +108 -0
- package/.claude/skills/otb-character-review/SKILL.md +71 -0
- package/.claude/skills/otb-design-review/SKILL.md +47 -0
- package/.claude/skills/otb-feedback-capture/SKILL.md +45 -0
- package/.claude/skills/otb-figma-sync/SKILL.md +54 -0
- package/.claude/skills/otb-tile-generate/SKILL.md +45 -0
- package/.claude/skills/paint/SKILL.md +70 -0
- package/.claude/skills/planning-session/SKILL.md +299 -0
- package/.claude/skills/priority-session/SKILL.md +134 -0
- package/.claude/skills/read-session-export/SKILL.md +58 -0
- package/.claude/skills/recall/SKILL.md +52 -0
- package/.claude/skills/session-handoff/SKILL.md +46 -0
- package/.claude/skills/status/SKILL.md +33 -0
- package/.devcontainer/Dockerfile +69 -0
- package/.devcontainer/README.md +73 -0
- package/.devcontainer/devcontainer-lock.json +19 -0
- package/.devcontainer/devcontainer.json +93 -0
- package/.devcontainer/healthcheck.sh +84 -0
- package/.devcontainer/init-firewall.sh +198 -0
- package/.devcontainer/post-create.sh +153 -0
- package/.dockerignore +36 -0
- package/.env.local.example +32 -0
- package/.gitattributes +83 -0
- package/.gitignore +91 -0
- package/CLAUDE.md +242 -0
- package/CODE_OF_CONDUCT.md +46 -0
- package/CONTRIBUTING.md +51 -0
- package/Dockerfile +46 -0
- package/GOVERNANCE.md +44 -0
- package/LICENSE +661 -0
- package/README.md +88 -0
- package/bin/bongos.js +222 -0
- package/clients/bongos-client/README.md +48 -0
- package/clients/bongos-client/bongos-client.global.js +617 -0
- package/clients/bongos-client/examples/hello-world.mjs +62 -0
- package/clients/bongos-client/index.cjs +619 -0
- package/clients/bongos-client/index.d.ts +793 -0
- package/clients/bongos-client/index.mjs +614 -0
- package/clients/bongos-client/package.json +32 -0
- package/config/branding.neutral.json +60 -0
- package/config/design-tokens.neutral.json +51 -0
- package/config/design-tokens.schema.json +96 -0
- package/config/modules.neutral.json +9 -0
- package/docker-compose.yml +52 -0
- package/docker-entrypoint.sh +30 -0
- package/docs/adr/0001-stack-choice-single-vps.md +45 -0
- package/docs/adr/0002-digitalocean-over-hetzner.md +27 -0
- package/docs/adr/0003-cloudflare-origin-cert-over-letsencrypt.md +35 -0
- package/docs/adr/0004-amazonprimea-name-and-trademark-acceptance.md +45 -0
- package/docs/adr/0005-invisible-other-players-in-v1.md +37 -0
- package/docs/adr/0006-singleton-worldroom-plus-queueroom.md +30 -0
- package/docs/adr/0007-persistent-identity-via-localstorage-uuid.md +31 -0
- package/docs/adr/0008-family-based-tile-generation.md +86 -0
- package/docs/adr/0008-google-chat-oauth-user-auth.md +37 -0
- package/docs/adr/0009-autotile-architecture.md +181 -0
- package/docs/adr/0010-pms-architecture.md +70 -0
- package/docs/adr/0011-terrain-id-rendering-model.md +174 -0
- package/docs/adr/0012-ground-object-cell-split.md +208 -0
- package/docs/adr/0013-paths-as-object-overlays-with-autotile-dispatch.md +158 -0
- package/docs/adr/0014-families-canonical-over-vocabulary.md +103 -0
- package/docs/adr/0015-task-dependencies-and-auto-promotion.md +125 -0
- package/docs/adr/0016-trust-boundary-server-enforced-permissions.md +132 -0
- package/docs/adr/0017-code-simplification-and-repo-cleanliness.md +74 -0
- package/docs/adr/0018-three-rank-model-goes-live.md +97 -0
- package/docs/adr/0019-red-team-bounty-payout-table.md +52 -0
- package/docs/adr/0020-security-model-public-stub.md +70 -0
- package/docs/adr/0021-per-builder-skill-model-preferences.md +99 -0
- package/docs/adr/0022-secrets-policy.md +124 -0
- package/docs/adr/0023-kind-multipliers-and-idea-bonus.md +118 -0
- package/docs/adr/0024-cloneable-repo-local-first-memory.md +277 -0
- package/docs/adr/0024-multi-agent-system-architecture.md +125 -0
- package/docs/adr/0025-offsite-backup-vendor-digitalocean-spaces.md +59 -0
- package/docs/adr/0025-structured-criterion-task-link.md +44 -0
- package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +117 -0
- package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +170 -0
- package/docs/adr/0028-honest-llm-spend-accounting.md +109 -0
- package/docs/adr/0029-status-page-outage-resilience.md +59 -0
- package/docs/adr/0030-builders-hall-live-updates.md +59 -0
- package/docs/adr/0031-cloud-dev-environments-for-builders.md +222 -0
- package/docs/adr/0032-discord-mirror-of-gds.md +50 -0
- package/docs/adr/0033-discord-bot-service-principal.md +82 -0
- package/docs/adr/0034-thetes-graduated-newcomer-rank.md +53 -0
- package/docs/adr/0035-builder-onboarding-three-paths.md +128 -0
- package/docs/adr/0036-builders-subdomain-and-ui-reorg.md +40 -0
- package/docs/adr/0037-discord-channel-management.md +51 -0
- package/docs/adr/0038-chromebook-ttyd-cloudflare-tunnel.md +111 -0
- package/docs/adr/0039-setup-first-onboarding-ux.md +49 -0
- package/docs/adr/0040-remote-control-default-browser-onramp.md +47 -0
- package/docs/adr/0041-temporary-grader-bypass-killswitch.md +40 -0
- package/docs/adr/0042-builder-self-deploy-ci-auto-merge.md +82 -0
- package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +74 -0
- package/docs/adr/0044-mediterranean-palette-replacement.md +73 -0
- package/docs/adr/0044-per-box-live-game-preview.md +57 -0
- package/docs/adr/0045-devbox-desktop-app.md +133 -0
- package/docs/adr/0046-sandbox-first-review-gate.md +72 -0
- package/docs/adr/0047-discord-bugs-intake-anti-abuse.md +36 -0
- package/docs/adr/0048-works-in-progress-ia-split.md +32 -0
- package/docs/adr/0049-split-parallel-safety-contract.md +46 -0
- package/docs/adr/0050-device-flow-admission-invite-gated-by-default.md +43 -0
- package/docs/adr/0051-full-session-transcript-corpus.md +57 -0
- package/docs/adr/0052-sandbox-for-everyone-game-only-preview.md +70 -0
- package/docs/adr/0053-scoped-dev-box-session.md +55 -0
- package/docs/adr/0054-session-token-reward-cost-plus.md +63 -0
- package/docs/adr/0055-server-mediated-branch-publish.md +47 -0
- package/docs/adr/0056-prod-deploy-script-mirror-and-healthcheck-retry.md +62 -0
- package/docs/adr/0057-container-cost-ledger.md +28 -0
- package/docs/adr/0057-discord-archon-approval-channels.md +64 -0
- package/docs/adr/0058-automatic-gate-surface-gatekeeper.md +64 -0
- package/docs/adr/0059-single-approval-remove-devbox-approval-gate.md +37 -0
- package/docs/adr/0060-gds-retrieval-layer.md +97 -0
- package/docs/adr/0061-context-layer-decomposition.md +61 -0
- package/docs/adr/0062-medusa-instance-model-decoupling.md +107 -0
- package/docs/adr/0063-generated-repo-map-symbol-skeleton.md +44 -0
- package/docs/adr/0064-rename-medusa-to-cloud-bongos.md +49 -0
- package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +69 -0
- package/docs/adr/0066-generated-file-map-sections.md +51 -0
- package/docs/adr/0067-session-start-fast-forward-pull.md +43 -0
- package/docs/adr/0068-shared-art-key-for-newcomers.md +38 -0
- package/docs/adr/0069-hall-live-channel-sse-decouple.md +35 -0
- package/docs/adr/0070-deterministic-session-card-delivery.md +44 -0
- package/docs/adr/0071-box-confirm-before-destroyed-and-drift-reconcile.md +59 -0
- package/docs/adr/0072-bongos-app-mac-signed-first-windows-deferred.md +35 -0
- package/docs/adr/0072-dev-box-code-staleness-visibility.md +39 -0
- package/docs/adr/0073-builder-needs-signal-and-byok-gemini-key.md +64 -0
- package/docs/adr/0073-secrets-scan-exclude-uri-detector.md +37 -0
- package/docs/adr/0074-memory-map-stays-light.md +32 -0
- package/docs/adr/0075-per-system-cost-attribution.md +37 -0
- package/docs/adr/0076-in-session-deterministic-compaction.md +58 -0
- package/docs/adr/0077-content-addressed-llm-cache.md +32 -0
- package/docs/adr/0078-deterministic-first-cron.md +48 -0
- package/docs/adr/0079-value-gated-autonomous-engagement.md +39 -0
- package/docs/adr/0080-llm-cache-api-transport-and-trust-model.md +46 -0
- package/docs/adr/0081-tool-agnostic-design-layer.md +62 -0
- package/docs/adr/0082-productized-key-storage-provisioning.md +32 -0
- package/docs/adr/0082-server-side-merge-conflict-auto-resolution.md +55 -0
- package/docs/adr/0083-modular-architecture-strangler-build-plan.md +143 -0
- package/docs/adr/0084-auto-derived-per-task-rank-floor.md +86 -0
- package/docs/adr/0085-remove-default-per-builder-spend-cap.md +39 -0
- package/docs/adr/0086-goal-scoped-work-hierarchy.md +123 -0
- package/docs/adr/0087-bongos-app-architecture-and-handoff-contract.md +49 -0
- package/docs/adr/0087-compete-on-governance-not-tooling-cursor.md +65 -0
- package/docs/adr/0088-consistent-character-generative-animation.md +169 -0
- package/docs/adr/0089-modular-multi-model-adversarial-grader.md +179 -0
- package/docs/adr/0090-metic-task-authoring.md +73 -0
- package/docs/adr/0091-bounding-the-kernel-and-db-carve.md +106 -0
- package/docs/adr/0092-hosting-module-and-on-prem-target.md +117 -0
- package/docs/adr/0093-tranche-2-core-carve-sequence.md +72 -0
- package/docs/adr/0094-repo-atlas-self-drawing-codebase-map.md +49 -0
- package/docs/adr/0095-borrowed-memory-and-retrieval-concepts.md +65 -0
- package/docs/adr/0095-cross-agent-context-management.md +58 -0
- package/docs/adr/0096-require-reward-before-workable.md +209 -0
- package/docs/adr/0097-one-active-claim-per-session-and-worktree-binding.md +46 -0
- package/docs/adr/0097-retroactive-reward-backfill.md +124 -0
- package/docs/adr/0098-public-mirror-publish-manifest.md +92 -0
- package/docs/adr/0099-delayed-redacted-mirror-export.md +55 -0
- package/docs/adr/0100-two-repo-instance-model.md +60 -0
- package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +90 -0
- package/docs/adr/0102-finish-bounding-the-kernel.md +36 -0
- package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +70 -0
- package/docs/adr/0103-gdsv4-bongos-consolidation.md +28 -0
- package/docs/adr/0104-trust-gds-api-channel-in-auto-mode.md +42 -0
- package/docs/adr/0105-instance-seed-migrations-out-of-core.md +47 -0
- package/docs/adr/0106-goal-owner-manager-authority.md +84 -0
- package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +83 -0
- package/docs/adr/0108-instance-composition-configurable-root.md +87 -0
- package/docs/adr/0109-self-describing-openapi-and-hosted-docs.md +44 -0
- package/docs/adr/0110-untrack-whole-file-generated-nav-docs.md +49 -0
- package/docs/adr/0111-instance-hosting-provisioning-module.md +135 -0
- package/docs/adr/0112-private-goals-invitations-join-requests.md +72 -0
- package/docs/adr/0113-sandbox-studio-descope-superseded-by-0044-0052.md +41 -0
- package/docs/adr/0114-instance-docs-discovery-contract.md +32 -0
- package/docs/adr/0115-scheduled-routines-portability.md +31 -0
- package/docs/adr/0116-api-error-envelope-and-code-taxonomy.md +62 -0
- package/docs/adr/0117-api-versioning-and-deprecation-policy.md +50 -0
- package/docs/adr/0118-api-contract-source-of-truth-code-first.md +47 -0
- package/docs/adr/0119-api-pagination-contract.md +34 -0
- package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +108 -0
- package/docs/adr/0121-greenfield-vs-brownfield-onboarding-adopt-existing-repo.md +97 -0
- package/docs/adr/0122-onboarding-diagram-renders-deploy-rendered-off-git.md +43 -0
- package/docs/adr/0123-box-idle-sweep-autosave-before-destroy.md +36 -0
- package/docs/adr/0124-typed-api-response-bodies.md +37 -0
- package/docs/adr/0125-control-plane-scaffolds-standalone-instance-repo.md +36 -0
- package/docs/adr/0126-dedicated-cloudbongos-control-plane-droplet.md +30 -0
- package/docs/adr/0127-cross-box-fleet-observability.md +64 -0
- package/docs/adr/0128-provisioning-runner-privilege-and-db-auth-model.md +46 -0
- package/docs/adr/0129-cloudbongos-control-plane-pull-deploy.md +34 -0
- package/docs/adr/0130-provision-remote-exec-over-ssh.md +61 -0
- package/docs/adr/0131-rank-scoped-skill-visibility.md +47 -0
- package/docs/adr/0132-cohost-fleet-control-plane-migration.md +54 -0
- package/docs/adr/0133-github-app-manifest-onboarding-signin.md +42 -0
- package/docs/adr/0134-private-first-npm-distribution.md +38 -0
- package/docs/adr/README.md +208 -0
- package/docs/api/openapi.json +13720 -0
- package/docs/api-reference.md +437 -0
- package/docs/architecture.md +244 -0
- package/docs/branding-contract.md +66 -0
- package/docs/branding-fill-prompt.md +25 -0
- package/docs/canonical-permissions.md +241 -0
- package/docs/design/container-exterior-tiles.md +49 -0
- package/docs/design/container-exterior.md +81 -0
- package/docs/design/container-interior.md +77 -0
- package/docs/design/modular-architecture/00-research-report.md +107 -0
- package/docs/design/modular-architecture/01-architecture.md +125 -0
- package/docs/design/modular-architecture/02-module-map.md +116 -0
- package/docs/design/modular-architecture/03-builder-flows.md +104 -0
- package/docs/design/modular-architecture/04-module-lifecycle.md +62 -0
- package/docs/design/modular-architecture/README.md +27 -0
- package/docs/design/vanilla-hall-ui-redesign-scope.md +284 -0
- package/docs/design/world-map-scroll.md +41 -0
- package/docs/design-contract.md +320 -0
- package/docs/file-map.md +418 -0
- package/docs/handoff-template.md +92 -0
- package/docs/modules-contract.md +293 -0
- package/docs/onboarding/browser-terminal-guide.md +73 -0
- package/docs/onboarding/diagrams/01-task-lifecycle.mmd +28 -0
- package/docs/onboarding/diagrams/02-rank-ladder.mmd +54 -0
- package/docs/onboarding/diagrams/03-drachmae-karma.mmd +37 -0
- package/docs/onboarding/diagrams/04-architecture.mmd +51 -0
- package/docs/onboarding/diagrams/05-versioning.mmd +29 -0
- package/docs/onboarding/diagrams/README.md +94 -0
- package/docs/onboarding/diagrams/assertions.json +170 -0
- package/docs/onboarding/diagrams/puppeteer-config.json +1 -0
- package/docs/onboarding/diagrams/render-config.json +9 -0
- package/docs/onboarding/drachmae-and-karma.md +178 -0
- package/docs/onboarding/primer.md +202 -0
- package/docs/project-context.template.md +67 -0
- package/docs/recipes/README.md +35 -0
- package/docs/recipes/audit-replay.md +100 -0
- package/docs/recipes/bongos-cli-release.md +66 -0
- package/docs/recipes/builder-box-lifecycle.md +152 -0
- package/docs/recipes/claude-design-loop.md +143 -0
- package/docs/recipes/claude-gemini-bongos-doors.md +155 -0
- package/docs/recipes/cloudbongos-staging-gate.md +139 -0
- package/docs/recipes/cloudbongos-standup.md +111 -0
- package/docs/recipes/devbox-app-release.md +93 -0
- package/docs/recipes/devbox-app-windows-test.md +128 -0
- package/docs/recipes/failed-ship-recovery-proof.md +102 -0
- package/docs/recipes/figma-design-loop.md +129 -0
- package/docs/recipes/gds-db-backup.md +255 -0
- package/docs/recipes/instance-deploy.md +37 -0
- package/docs/recipes/managed-settings-remote-control.md +416 -0
- package/docs/recipes/memory-backup.md +71 -0
- package/docs/recipes/memory-sync-conflicts.md +98 -0
- package/docs/recipes/multi-builder-merge.md +78 -0
- package/docs/recipes/ops-gotchas.md +212 -0
- package/docs/recipes/overnight-builder.md +53 -0
- package/docs/recipes/packaging-the-core.md +49 -0
- package/docs/recipes/parallel-load-results-2026-05-30.md +63 -0
- package/docs/recipes/parallel-load-test.md +32 -0
- package/docs/recipes/pixel-art-pipeline.md +411 -0
- package/docs/recipes/private-npm-distribution.md +79 -0
- package/docs/recipes/search-index-ingestion.md +67 -0
- package/docs/recipes/self-host.md +60 -0
- package/docs/recipes/ship-and-regrade.md +72 -0
- package/docs/recipes/staging-data.md +50 -0
- package/docs/recipes/staging-environment.md +215 -0
- package/docs/recipes/upgrading-the-core.md +58 -0
- package/docs/research/cursor-competitive-analysis.md +255 -0
- package/docs/research/llm-model-agnostic-and-cheaper-grader.md +227 -0
- package/migrations/003_pms.sql +284 -0
- package/migrations/004_pms_cost_idempotency.sql +13 -0
- package/migrations/005_pms_smoke_filter.sql +44 -0
- package/migrations/006_pms_v2.sql +378 -0
- package/migrations/007_pms_v2_hierarchy_trigger.sql +59 -0
- package/migrations/008_pms_v2_migration_allocator.sql +27 -0
- package/migrations/009_pms_v2_blockers_table.sql +68 -0
- package/migrations/010_uptime.sql +29 -0
- package/migrations/011_backfill_v2_v3_done_when.sql +20 -0
- package/migrations/012_pms_v2_task_lifecycle_states.sql +82 -0
- package/migrations/013_claim_locking.sql +22 -0
- package/migrations/014_pms_task_discipline.sql +86 -0
- package/migrations/015_pms_estimation_calibration.sql +140 -0
- package/migrations/016_pms_task_grades.sql +60 -0
- package/migrations/017_version_progress_exclude_abandoned.sql +49 -0
- package/migrations/019_rename_pms_v3_to_gds_v3.sql +65 -0
- package/migrations/020_task_dependencies.sql +99 -0
- package/migrations/021_v3_renumber_and_deps.sql +224 -0
- package/migrations/022_v3_reconcile_dep_gated_status.sql +41 -0
- package/migrations/023_pms_rank.sql +30 -0
- package/migrations/025_builder_exit_status.sql +15 -0
- package/migrations/027_builders_preferred_disciplines.sql +46 -0
- package/migrations/028_tasks_parallel_safe.sql +71 -0
- package/migrations/029_rank_three_live.sql +32 -0
- package/migrations/030_vulnerability_reports.sql +61 -0
- package/migrations/031_reward_simplification.sql +57 -0
- package/migrations/032_repo_health_snapshots.sql +56 -0
- package/migrations/033_audit_log.sql +64 -0
- package/migrations/034_lifecycle_breakdown_leaf_only.sql +62 -0
- package/migrations/035_auth_token_hygiene.sql +66 -0
- package/migrations/037_builder_skill_model_prefs.sql +44 -0
- package/migrations/038_task_override_requests.sql +51 -0
- package/migrations/039_newcomer_friendly_column.sql +124 -0
- package/migrations/040_builder_karma.sql +58 -0
- package/migrations/041_kind_multipliers_and_idea_bonus.sql +42 -0
- package/migrations/042_criterion_proposal_workflow.sql +78 -0
- package/migrations/043_builder_onboarding_observability.sql +66 -0
- package/migrations/044_tasks_requires_rank.sql +97 -0
- package/migrations/045_builder_onboarding_state.sql +147 -0
- package/migrations/046_cost_log_builder_attribution.sql +42 -0
- package/migrations/054_builder_memory.sql +108 -0
- package/migrations/055_task_criteria.sql +90 -0
- package/migrations/058_peer_votes.sql +41 -0
- package/migrations/059_onboarding_pick_disciplines.sql +59 -0
- package/migrations/060_access_requests.sql +46 -0
- package/migrations/061_backfill_override_ship_attribution.sql +29 -0
- package/migrations/062_session_records.sql +34 -0
- package/migrations/063_session_records_tool_errors_idx.sql +11 -0
- package/migrations/064_grade_attempts.sql +18 -0
- package/migrations/065_bfg_principal.sql +58 -0
- package/migrations/066_live_notify_triggers.sql +69 -0
- package/migrations/067_builder_boxes.sql +117 -0
- package/migrations/067_discord_bot_foundation.sql +69 -0
- package/migrations/068_thetes_requires_rank.sql +27 -0
- package/migrations/069_builder_ssh_keys.sql +66 -0
- package/migrations/070_box_intents.sql +101 -0
- package/migrations/071_box_first_provision_approval.sql +46 -0
- package/migrations/072_onboarding_ship_three_remove_primer.sql +80 -0
- package/migrations/073_box_terminal_access.sql +46 -0
- package/migrations/075_builder_sound_prefs.sql +28 -0
- package/migrations/078_box_claude_active.sql +22 -0
- package/migrations/079_box_first_connected_at.sql +20 -0
- package/migrations/081_tasks_needs_migration.sql +34 -0
- package/migrations/084_tasks_security_sensitive.sql +24 -0
- package/migrations/085_security_engagement_docs.sql +34 -0
- package/migrations/086_rename_disciplines.sql +125 -0
- package/migrations/088_builders_anthropic_link.sql +28 -0
- package/migrations/089_builders_monthly_budget.sql +19 -0
- package/migrations/090_box_scoped_sessions.sql +38 -0
- package/migrations/091_session_token_reward.sql +44 -0
- package/migrations/093_tasks_sandbox_review.sql +22 -0
- package/migrations/102_remove_box_first_provision_approval.sql +58 -0
- package/migrations/103_search_chunks.sql +90 -0
- package/migrations/104_builder_box_blocked.sql +34 -0
- package/migrations/105_box_host_keys.sql +35 -0
- package/migrations/111_medusa_customer0_framing.sql +35 -0
- package/migrations/112_drop_track_check.sql +25 -0
- package/migrations/113_merge_lease.sql +16 -0
- package/migrations/114_cloudbongos_rename_live_strings.sql +32 -0
- package/migrations/118_currency_label_decoupling.sql +23 -0
- package/migrations/119_builder_behavior_prefs.sql +31 -0
- package/migrations/121_box_code_version.sql +37 -0
- package/migrations/122_builder_gemini_key.sql +30 -0
- package/migrations/123_cost_log_tiers.sql +33 -0
- package/migrations/124_cost_baseline.sql +45 -0
- package/migrations/125_llm_cache.sql +54 -0
- package/migrations/126_autonomy_runs.sql +33 -0
- package/migrations/143_cost_calibration.sql +63 -0
- package/migrations/145_backfill_task_requires_rank.sql +84 -0
- package/migrations/160_goal_tier.sql +88 -0
- package/migrations/163_polymorphic_dependencies.sql +187 -0
- package/migrations/164_event_sound_prefs.sql +20 -0
- package/migrations/165_task_module_key.sql +15 -0
- package/migrations/171_claims_published_head_sha.sql +24 -0
- package/migrations/174_close_orphaned_claims_on_terminal_tasks.sql +30 -0
- package/migrations/175_task_grades_diff_hash.sql +9 -0
- package/migrations/176_promotion_respects_open_blockers.sql +58 -0
- package/migrations/176_ui_discipline.sql +40 -0
- package/migrations/177_require_reward_before_workable.sql +76 -0
- package/migrations/178_reward_gate_auto_assign_revert_autopromote.sql +61 -0
- package/migrations/179_search_chunk_usefulness.sql +33 -0
- package/migrations/180_one_active_claim_per_session.sql +54 -0
- package/migrations/181_cost_log_skill_name.sql +33 -0
- package/migrations/184_claim_release_requested.sql +25 -0
- package/migrations/185_module_upstreaming_goal_wiring.sql +59 -0
- package/migrations/187_goal_sort_order.sql +30 -0
- package/migrations/187_tasks_kind_check_add_verify.sql +21 -0
- package/migrations/README.md +88 -0
- package/migrations/core_188_goal_visibility_and_membership_requests.sql +71 -0
- package/migrations/core_189_core_upgrades_ledger.sql +37 -0
- package/migrations/core_189_credit_log_task_id_index.sql +19 -0
- package/migrations/core_190_rebuild_claimable_task_views.sql +71 -0
- package/migrations/instance/018_pms_v3_planning_seed.sql +131 -0
- package/migrations/instance/080_gds_v4_planning_seed.sql +75 -0
- package/migrations/instance/110_medusa_planning_seed.sql +70 -0
- package/migrations/instance/120_bongos_v1_planning_seed.sql +69 -0
- package/migrations/instance/144_bongos_v1_module_system_criterion.sql +26 -0
- package/migrations/instance/159_bongos_v1_goal_hierarchy_criterion.sql +29 -0
- package/migrations/instance/172_v2_world_planning_seed.sql +163 -0
- package/migrations/instance/173_bongos_v1_c7_core_modularized.sql +33 -0
- package/migrations/instance/182_gdsv4_bongos_consolidation.sql +132 -0
- package/migrations/instance/183_gdsv4_carryover_themed_goals.sql +116 -0
- package/migrations/instance/186_bongos_v1_governance_planning_seed.sql +69 -0
- package/modules/autonomy/CLAUDE.md +49 -0
- package/modules/autonomy/db.js +50 -0
- package/modules/autonomy/module.json +13 -0
- package/modules/autonomy/pollers/routine-timers.js +146 -0
- package/modules/autonomy/routes/autonomy.js +37 -0
- package/modules/builder-settings/CLAUDE.md +58 -0
- package/modules/builder-settings/builder-needs.js +115 -0
- package/modules/builder-settings/db.js +168 -0
- package/modules/builder-settings/display-name.js +84 -0
- package/modules/builder-settings/event-sound-prefs.js +169 -0
- package/modules/builder-settings/interaction-prefs.js +201 -0
- package/modules/builder-settings/model-alloc-prefs.js +147 -0
- package/modules/builder-settings/module.json +10 -0
- package/modules/builder-settings/render-prefs.js +103 -0
- package/modules/builder-settings/routes/settings.js +417 -0
- package/modules/builder-settings/settings.js +52 -0
- package/modules/builder-settings/skill-prefs.js +196 -0
- package/modules/builder-settings/sound-prefs.js +178 -0
- package/modules/builder-settings/wandering-prefs.js +282 -0
- package/modules/dev-box/CLAUDE.md +38 -0
- package/modules/dev-box/box-access.js +275 -0
- package/modules/dev-box/box-credential.js +94 -0
- package/modules/dev-box/box-onboard.js +486 -0
- package/modules/dev-box/boxes.js +842 -0
- package/modules/dev-box/db.js +39 -0
- package/modules/dev-box/module.json +14 -0
- package/modules/dev-box/routes/box.js +977 -0
- package/modules/discord/CLAUDE.md +50 -0
- package/modules/discord/auth.js +54 -0
- package/modules/discord/bug-attachments.js +210 -0
- package/modules/discord/db.js +96 -0
- package/modules/discord/discord-approvals.js +145 -0
- package/modules/discord/discord-audit.js +29 -0
- package/modules/discord/discord-bot.js +545 -0
- package/modules/discord/discord-bugs.js +257 -0
- package/modules/discord/discord-channels.js +412 -0
- package/modules/discord/discord-inbound.js +147 -0
- package/modules/discord/discord-message-buffer.js +54 -0
- package/modules/discord/discord-reaction.js +186 -0
- package/modules/discord/discord-role-sync.js +318 -0
- package/modules/discord/module.json +14 -0
- package/modules/discord/pollers/discord-bot.js +19 -0
- package/modules/discord/routes/bug-attachments.js +44 -0
- package/modules/discord/routes/discord.js +102 -0
- package/modules/discord/ship-broadcast.js +78 -0
- package/modules/discord/webhook.js +74 -0
- package/modules/economy/CLAUDE.md +48 -0
- package/modules/economy/achievements.js +594 -0
- package/modules/economy/cost-classify.js +331 -0
- package/modules/economy/cost-summary-shape.js +90 -0
- package/modules/economy/cost.js +438 -0
- package/modules/economy/credits.js +430 -0
- package/modules/economy/migrations/economy_001_fix_gds_shipper_description.sql +91 -0
- package/modules/economy/module.json +10 -0
- package/modules/economy/reward.js +61 -0
- package/modules/economy/routes/cost.js +97 -0
- package/modules/economy/routes/leaderboard.js +26 -0
- package/modules/economy/routes/reward.js +42 -0
- package/modules/economy/streak.js +285 -0
- package/modules/grading/CLAUDE.md +42 -0
- package/modules/grading/db.js +39 -0
- package/modules/grading/grader-rubric.json +151 -0
- package/modules/grading/grader-workers/efficiency.js +213 -0
- package/modules/grading/grader-workers/extract-json.js +80 -0
- package/modules/grading/grader-workers/fence.js +100 -0
- package/modules/grading/grader-workers/hacker.js +239 -0
- package/modules/grading/grader-workers/index.js +183 -0
- package/modules/grading/grader-workers/narc.js +207 -0
- package/modules/grading/grader-workers/quality.js +198 -0
- package/modules/grading/grader-workers/rubric.js +20 -0
- package/modules/grading/grader-workers/worker-prompt.js +89 -0
- package/modules/grading/grader-workers/worker-verdict.js +50 -0
- package/modules/grading/grader.js +1790 -0
- package/modules/grading/module.json +12 -0
- package/modules/grading/routes/grade.js +51 -0
- package/modules/hall-ui/CLAUDE.md +61 -0
- package/modules/hall-ui/module.json +14 -0
- package/modules/hall-ui/public/atlas.html +180 -0
- package/modules/hall-ui/public/atlas.js +336 -0
- package/modules/hall-ui/public/box-panel.js +423 -0
- package/modules/hall-ui/public/brand-apply.js +117 -0
- package/modules/hall-ui/public/builders.js +3028 -0
- package/modules/hall-ui/public/chime.wav +0 -0
- package/modules/hall-ui/public/copy-on-click.js +174 -0
- package/modules/hall-ui/public/cursors/arrow.png +0 -0
- package/modules/hall-ui/public/cursors/hand-pointer.png +0 -0
- package/modules/hall-ui/public/cursors/magnifier-out.png +0 -0
- package/modules/hall-ui/public/cursors/magnifier.png +0 -0
- package/modules/hall-ui/public/diagram-viewer.css +125 -0
- package/modules/hall-ui/public/diagram-viewer.js +330 -0
- package/modules/hall-ui/public/diagrams.html +113 -0
- package/modules/hall-ui/public/dom-utils.js +212 -0
- package/modules/hall-ui/public/drachmae.html +68 -0
- package/modules/hall-ui/public/drachmae.js +136 -0
- package/modules/hall-ui/public/gate.html +143 -0
- package/modules/hall-ui/public/gate.js +228 -0
- package/modules/hall-ui/public/goals-page.js +1297 -0
- package/modules/hall-ui/public/goals.html +376 -0
- package/modules/hall-ui/public/goals.js +119 -0
- package/modules/hall-ui/public/hall-widgets.js +189 -0
- package/modules/hall-ui/public/harbor.html +167 -0
- package/modules/hall-ui/public/harbor.js +335 -0
- package/modules/hall-ui/public/index.html +116 -0
- package/modules/hall-ui/public/instances-panel.js +172 -0
- package/modules/hall-ui/public/md-reader.js +234 -0
- package/modules/hall-ui/public/modules.html +119 -0
- package/modules/hall-ui/public/modules.js +154 -0
- package/modules/hall-ui/public/new-project-wizard.js +186 -0
- package/modules/hall-ui/public/not-ready.html +48 -0
- package/modules/hall-ui/public/pair.html +119 -0
- package/modules/hall-ui/public/pair.js +132 -0
- package/modules/hall-ui/public/primer.css +404 -0
- package/modules/hall-ui/public/primer.html +85 -0
- package/modules/hall-ui/public/primer.js +641 -0
- package/modules/hall-ui/public/profile.html +85 -0
- package/modules/hall-ui/public/profile.js +236 -0
- package/modules/hall-ui/public/ranks.html +69 -0
- package/modules/hall-ui/public/ranks.js +206 -0
- package/modules/hall-ui/public/roadmap.html +209 -0
- package/modules/hall-ui/public/roadmap.js +375 -0
- package/modules/hall-ui/public/sessions.html +92 -0
- package/modules/hall-ui/public/sessions.js +222 -0
- package/modules/hall-ui/public/settings.html +640 -0
- package/modules/hall-ui/public/settings.js +1219 -0
- package/modules/hall-ui/public/shell.js +495 -0
- package/modules/hall-ui/public/style.css +1748 -0
- package/modules/hall-ui/public/task-detail.js +253 -0
- package/modules/hall-ui/public/task.html +83 -0
- package/modules/hall-ui/public/task.js +326 -0
- package/modules/hall-ui/public/watch.html +333 -0
- package/modules/hall-ui/public/watch.js +859 -0
- package/modules/hall-ui/public/work.html +469 -0
- package/modules/hall-ui/public/work.js +1292 -0
- package/modules/ideas/CLAUDE.md +37 -0
- package/modules/ideas/blockers.js +100 -0
- package/modules/ideas/inbox.js +459 -0
- package/modules/ideas/module.json +10 -0
- package/modules/ideas/routes/blockers.js +120 -0
- package/modules/ideas/routes/inbox.js +230 -0
- package/modules/lifecycle/CLAUDE.md +63 -0
- package/modules/lifecycle/conflict-resolve.js +436 -0
- package/modules/lifecycle/db.js +4970 -0
- package/modules/lifecycle/done-when.js +355 -0
- package/modules/lifecycle/github-push.js +969 -0
- package/modules/lifecycle/goal-conflicts.js +133 -0
- package/modules/lifecycle/hierarchy.js +104 -0
- package/modules/lifecycle/lifecycle.js +58 -0
- package/modules/lifecycle/merge-lock.js +261 -0
- package/modules/lifecycle/migrations/lifecycle_001_goal_first_leads.sql +28 -0
- package/modules/lifecycle/migrations/lifecycle_002_backfill_orphan_task_goals.sql +36 -0
- package/modules/lifecycle/module.json +13 -0
- package/modules/lifecycle/pollers/publish-reconciler.js +20 -0
- package/modules/lifecycle/publish-reconciler.js +418 -0
- package/modules/lifecycle/routes/analytics.js +63 -0
- package/modules/lifecycle/routes/claims.js +506 -0
- package/modules/lifecycle/routes/dependencies.js +82 -0
- package/modules/lifecycle/routes/done-when.js +136 -0
- package/modules/lifecycle/routes/gate-approvals.js +197 -0
- package/modules/lifecycle/routes/goals.js +1419 -0
- package/modules/lifecycle/routes/lifecycle.js +43 -0
- package/modules/lifecycle/routes/tasks.js +1703 -0
- package/modules/lifecycle/routes/versions.js +128 -0
- package/modules/lifecycle/ship-card.js +505 -0
- package/modules/lifecycle/ship-preflight.js +225 -0
- package/modules/lifecycle/task-classifier.js +512 -0
- package/modules/lifecycle/version-close.js +106 -0
- package/modules/memory/CLAUDE.md +40 -0
- package/modules/memory/learnings.js +275 -0
- package/modules/memory/memory.js +572 -0
- package/modules/memory/module.json +12 -0
- package/modules/memory/routes/learnings.js +90 -0
- package/modules/memory/routes/memory.js +916 -0
- package/modules/memory/routes/search.js +188 -0
- package/modules/memory/search-chunker.js +252 -0
- package/modules/memory/search.js +320 -0
- package/modules/onboarding/CLAUDE.md +55 -0
- package/modules/onboarding/approval-broadcast.js +59 -0
- package/modules/onboarding/db.js +54 -0
- package/modules/onboarding/module.json +10 -0
- package/modules/onboarding/newcomer-restock.js +212 -0
- package/modules/onboarding/onboarding-state.js +532 -0
- package/modules/onboarding/onboarding.js +508 -0
- package/modules/onboarding/port.js +63 -0
- package/modules/onboarding/routes/access-requests.js +208 -0
- package/modules/onboarding/routes/onboarding.js +54 -0
- package/modules/security/CLAUDE.md +44 -0
- package/modules/security/module.json +10 -0
- package/modules/security/routes/reports.js +251 -0
- package/modules/security/secret-scrub.js +201 -0
- package/modules/security/security.js +294 -0
- package/modules/sessions/CLAUDE.md +39 -0
- package/modules/sessions/bfg.js +357 -0
- package/modules/sessions/db.js +326 -0
- package/modules/sessions/module.json +10 -0
- package/modules/sessions/pulse.js +149 -0
- package/modules/sessions/routes/sessions.js +331 -0
- package/modules/status-ui/CLAUDE.md +47 -0
- package/modules/status-ui/module.json +14 -0
- package/modules/status-ui/public/README.md +29 -0
- package/modules/status-ui/public/cursors/arrow.png +0 -0
- package/modules/status-ui/public/cursors/hand-pointer.png +0 -0
- package/modules/status-ui/public/index.html +363 -0
- package/modules/status-ui/public/snapshots/.gitkeep +0 -0
- package/modules/status-ui/public/status.js +1375 -0
- package/modules/status-ui/public/style.css +1342 -0
- package/package-lock.json +975 -0
- package/package.json +28 -0
- package/public-docs/index.html +35 -0
- package/public-docs/redoc.standalone.js +1838 -0
- package/public-docs/redoc.standalone.js.LICENSE.txt +102 -0
- package/public-landing/buddha.png +0 -0
- package/public-landing/create.html +868 -0
- package/public-landing/index.html +168 -0
- package/scripts/bongos-mcp.js +28 -0
- package/scripts/cron/style_guide_audit.sh +134 -0
- package/scripts/discord/README.md +52 -0
- package/scripts/discord/lib.js +80 -0
- package/scripts/discord/post.js +51 -0
- package/scripts/gds/CLAUDE.md +144 -0
- package/scripts/gds/abandon.js +58 -0
- package/scripts/gds/adopt-detect.js +123 -0
- package/scripts/gds/adopt-import.js +111 -0
- package/scripts/gds/adopt-preflight.js +121 -0
- package/scripts/gds/api.js +78 -0
- package/scripts/gds/architect-audit.js +288 -0
- package/scripts/gds/art-stage.js +152 -0
- package/scripts/gds/audit-authorship.sh +243 -0
- package/scripts/gds/audit-deps.js +121 -0
- package/scripts/gds/audit-exports.js +128 -0
- package/scripts/gds/audit-replay.sh +161 -0
- package/scripts/gds/audit-ships.js +251 -0
- package/scripts/gds/autonomy-gate.js +135 -0
- package/scripts/gds/backfill-cost.js +165 -0
- package/scripts/gds/backfill-sessions.js +351 -0
- package/scripts/gds/backfill-task-rewards.js +408 -0
- package/scripts/gds/backup.js +94 -0
- package/scripts/gds/bfg-principal-token.js +65 -0
- package/scripts/gds/box-auth-check.js +160 -0
- package/scripts/gds/box-connect-lib.js +453 -0
- package/scripts/gds/box.js +1258 -0
- package/scripts/gds/build-bongos.js +94 -0
- package/scripts/gds/capture.js +123 -0
- package/scripts/gds/cf-tunnel.js +419 -0
- package/scripts/gds/ci-grade.js +82 -0
- package/scripts/gds/claim-recall.js +112 -0
- package/scripts/gds/claim.js +333 -0
- package/scripts/gds/classify-discipline.js +153 -0
- package/scripts/gds/claude-materialize.js +176 -0
- package/scripts/gds/cleanup-orphans.js +74 -0
- package/scripts/gds/cli-lib.js +518 -0
- package/scripts/gds/code.js +101 -0
- package/scripts/gds/codemod-rename-src-gds.js +145 -0
- package/scripts/gds/conductor-dispatch.js +426 -0
- package/scripts/gds/context-pack.js +228 -0
- package/scripts/gds/coreB-carve-proof.sh +716 -0
- package/scripts/gds/cost.js +47 -0
- package/scripts/gds/db-backup-nightly.sh +160 -0
- package/scripts/gds/db-backup-offsite.sh +152 -0
- package/scripts/gds/demote.js +40 -0
- package/scripts/gds/design-sync.js +62 -0
- package/scripts/gds/diagnose-achievements.js +118 -0
- package/scripts/gds/diagram-facts.js +319 -0
- package/scripts/gds/discipline-modes.json +25 -0
- package/scripts/gds/discord-channels.js +66 -0
- package/scripts/gds/do-api.js +176 -0
- package/scripts/gds/docs-entropy.js +403 -0
- package/scripts/gds/doctor.js +389 -0
- package/scripts/gds/feedback-latest.js +94 -0
- package/scripts/gds/fetch-art-key.js +106 -0
- package/scripts/gds/fetch-sound-prefs.js +88 -0
- package/scripts/gds/figma-design-sync.js +61 -0
- package/scripts/gds/fitness.js +1446 -0
- package/scripts/gds/gate-review.js +322 -0
- package/scripts/gds/gating.js +325 -0
- package/scripts/gds/gds-db-backup.service +24 -0
- package/scripts/gds/gds-db-backup.timer +20 -0
- package/scripts/gds/gds-db-offsite.service +29 -0
- package/scripts/gds/gds-db-offsite.timer +18 -0
- package/scripts/gds/gen-api-client.js +508 -0
- package/scripts/gds/gen-api-docs.js +1037 -0
- package/scripts/gds/gen-atlas.js +366 -0
- package/scripts/gds/gen-diagrams.js +608 -0
- package/scripts/gds/gen-file-map.js +196 -0
- package/scripts/gds/gen-repo-map.js +519 -0
- package/scripts/gds/gen-session-index.js +210 -0
- package/scripts/gds/git-merge-regen.js +147 -0
- package/scripts/gds/grade-smoke.js +205 -0
- package/scripts/gds/hooks/pre-push.js +169 -0
- package/scripts/gds/http-api-client.js +88 -0
- package/scripts/gds/init.js +903 -0
- package/scripts/gds/install-git-hooks.js +114 -0
- package/scripts/gds/key-card.js +213 -0
- package/scripts/gds/key-status.js +126 -0
- package/scripts/gds/land-watch.js +168 -0
- package/scripts/gds/learning-capture.js +145 -0
- package/scripts/gds/lib.sh +44 -0
- package/scripts/gds/linkify-refs.js +473 -0
- package/scripts/gds/local-preview-lib.js +159 -0
- package/scripts/gds/local-preview.js +95 -0
- package/scripts/gds/login.js +225 -0
- package/scripts/gds/main-audit.js +279 -0
- package/scripts/gds/me.js +88 -0
- package/scripts/gds/memory-map.js +174 -0
- package/scripts/gds/merge-lock.js +71 -0
- package/scripts/gds/migration-namespace.js +200 -0
- package/scripts/gds/mirror-lineage-check.js +319 -0
- package/scripts/gds/mirror-redact.js +305 -0
- package/scripts/gds/module.js +482 -0
- package/scripts/gds/newcomer-floor.config.json +13 -0
- package/scripts/gds/newcomer-floor.js +415 -0
- package/scripts/gds/newcomer-floor.templates.json +86 -0
- package/scripts/gds/newcomer-floor.test.js +235 -0
- package/scripts/gds/next-number.js +163 -0
- package/scripts/gds/oauth-secret.js +197 -0
- package/scripts/gds/onboard.js +217 -0
- package/scripts/gds/onboarding-analyze.js +226 -0
- package/scripts/gds/onboarding-config.js +178 -0
- package/scripts/gds/onboarding-ship.js +92 -0
- package/scripts/gds/package-core.js +684 -0
- package/scripts/gds/paste-token.js +131 -0
- package/scripts/gds/portability-smoke-art.sh +85 -0
- package/scripts/gds/portability-smoke-memory.sh +147 -0
- package/scripts/gds/provision.js +1324 -0
- package/scripts/gds/publish-manifest.js +307 -0
- package/scripts/gds/pulse.js +309 -0
- package/scripts/gds/push-main.js +45 -0
- package/scripts/gds/read-session-export.js +359 -0
- package/scripts/gds/reauth.js +149 -0
- package/scripts/gds/recall.js +135 -0
- package/scripts/gds/release.js +71 -0
- package/scripts/gds/render-ideas.js +173 -0
- package/scripts/gds/repo-metrics.js +363 -0
- package/scripts/gds/resolve-skill-model.js +57 -0
- package/scripts/gds/run-routine.js +197 -0
- package/scripts/gds/run-unit-tests.js +178 -0
- package/scripts/gds/sandbox-stage.js +653 -0
- package/scripts/gds/search-ingest-db.js +209 -0
- package/scripts/gds/search-ingest-md.js +225 -0
- package/scripts/gds/security-baseline-check.sh +180 -0
- package/scripts/gds/seed-adopt-tasks.js +75 -0
- package/scripts/gds/seed-api-contract-goal-tasks.js +141 -0
- package/scripts/gds/seed-bongos-app-tasks.js +294 -0
- package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +81 -0
- package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +100 -0
- package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +307 -0
- package/scripts/gds/seed-bongos-module-system-tasks.js +346 -0
- package/scripts/gds/seed-bongos-tasks.js +214 -0
- package/scripts/gds/seed-gds-v4-tasks.js +624 -0
- package/scripts/gds/seed-governance-tasks.js +340 -0
- package/scripts/gds/seed-medusa-tasks.js +440 -0
- package/scripts/gds/seed-module-upstreaming.js +219 -0
- package/scripts/gds/seed-productization-tasks.js +235 -0
- package/scripts/gds/seed-provisioning-tasks.js +83 -0
- package/scripts/gds/seed-staging.sh +104 -0
- package/scripts/gds/seed-v2-world-tasks.js +283 -0
- package/scripts/gds/seed-v3-tasks.js +1981 -0
- package/scripts/gds/session-cost.js +393 -0
- package/scripts/gds/session-digest.js +179 -0
- package/scripts/gds/session-eval.js +576 -0
- package/scripts/gds/session.js +404 -0
- package/scripts/gds/setup.js +504 -0
- package/scripts/gds/shell.js +100 -0
- package/scripts/gds/ship.js +3618 -0
- package/scripts/gds/smoke-batch-claim.sh +213 -0
- package/scripts/gds/smoke-batch-release.sh +296 -0
- package/scripts/gds/smoke-box.sh +150 -0
- package/scripts/gds/smoke-claim-consistency.sh +212 -0
- package/scripts/gds/smoke-dependencies.sh +84 -0
- package/scripts/gds/smoke-gds.js +125 -0
- package/scripts/gds/smoke-write.sh +168 -0
- package/scripts/gds/start.js +782 -0
- package/scripts/gds/status.js +365 -0
- package/scripts/gds/triage.js +366 -0
- package/scripts/gds/upgrade.js +421 -0
- package/scripts/gds/validate-design.js +254 -0
- package/scripts/gds/version-close.js +549 -0
- package/scripts/migrate.sh +183 -0
- package/scripts/migrate_cost_ledger.sh +178 -0
- package/scripts/public-mirror-export.js +291 -0
- package/scripts/render-diagrams.sh +88 -0
- package/scripts/security/dep-audit.js +276 -0
- package/scripts/setup-builder.js +274 -0
- package/scripts/setup-builder.sh +28 -0
- package/scripts/snapshots/backfill-history.mjs +292 -0
- package/scripts/snapshots/maybe-render-on-deploy.sh +66 -0
- package/scripts/snapshots/render-pre-art-eras.mjs +329 -0
- package/scripts/snapshots/render-world.mjs +322 -0
- package/scripts/status-mirror-sync.js +192 -0
- package/scripts/status-probe.mjs +185 -0
- package/server.js +212 -0
- package/src/asset-version.js +71 -0
- package/src/bongos/CLAUDE.md +64 -0
- package/src/bongos/api-errors.js +281 -0
- package/src/bongos/api-path-404.js +67 -0
- package/src/bongos/api-prefix.js +38 -0
- package/src/bongos/app-pair.js +172 -0
- package/src/bongos/auth.js +979 -0
- package/src/bongos/cascade-dispatch.js +123 -0
- package/src/bongos/db-kernel.js +252 -0
- package/src/bongos/db.js +911 -0
- package/src/bongos/goal-scope-check.js +120 -0
- package/src/bongos/hierarchy-config.js +189 -0
- package/src/bongos/live-channel.js +145 -0
- package/src/bongos/llm-cache.js +260 -0
- package/src/bongos/llm-pricing.js +220 -0
- package/src/bongos/metrics.js +286 -0
- package/src/bongos/middleware/audit.js +274 -0
- package/src/bongos/middleware/error-envelope.js +93 -0
- package/src/bongos/middleware/rate-limit.js +195 -0
- package/src/bongos/module-scope-map.js +271 -0
- package/src/bongos/optimizer.js +166 -0
- package/src/bongos/path-match.js +47 -0
- package/src/bongos/permission-path-check.js +248 -0
- package/src/bongos/pool.js +55 -0
- package/src/bongos/repo-info.js +91 -0
- package/src/bongos/route-rank-check.js +420 -0
- package/src/bongos/routes/CLAUDE.md +43 -0
- package/src/bongos/routes/_helpers.js +369 -0
- package/src/bongos/routes/audit-log.js +59 -0
- package/src/bongos/routes/auth.js +755 -0
- package/src/bongos/routes/backup.js +104 -0
- package/src/bongos/routes/builders.js +526 -0
- package/src/bongos/routes/healthz.js +16 -0
- package/src/bongos/routes/instance.js +129 -0
- package/src/bongos/routes/live.js +125 -0
- package/src/bongos/routes/llm-cache.js +119 -0
- package/src/bongos/routes/me.js +380 -0
- package/src/bongos/routes/modules.js +131 -0
- package/src/bongos/routes/optimizer.js +54 -0
- package/src/bongos/routes/public.js +476 -0
- package/src/bongos/routes/security.js +207 -0
- package/src/bongos/routes.js +166 -0
- package/src/bongos/secret-box.js +177 -0
- package/src/bongos/sensitive-surfaces.js +30 -0
- package/src/bongos/serve-internal.js +889 -0
- package/src/bongos/uptime-poller.js +90 -0
- package/src/bongos-downloads.js +70 -0
- package/src/branding.js +182 -0
- package/src/build-info.js +77 -0
- package/src/devbox-downloads.js +67 -0
- package/src/instance-config.js +200 -0
- package/src/module-api.js +366 -0
- package/src/module-loader/loader.js +268 -0
- package/src/module-loader/manifest-schema.js +140 -0
- package/src/module-loader/provenance.js +28 -0
- package/src/module-loader/semver.js +100 -0
- package/src/module-seams.js +291 -0
- package/src/modules.js +254 -0
- package/src/platform-server.js +135 -0
- package/src/ui/adapters/claude-design/index.js +333 -0
- package/src/ui/adapters/figma/index.js +328 -0
- package/tests/abandon_task.mjs +138 -0
- package/tests/achievements_reconcile.mjs +167 -0
- package/tests/adopt_detect.mjs +141 -0
- package/tests/adopt_import.mjs +132 -0
- package/tests/adopt_preflight.mjs +105 -0
- package/tests/anti_grid.mjs +89 -0
- package/tests/api_client.mjs +231 -0
- package/tests/api_docs.mjs +283 -0
- package/tests/api_error_envelope_buildcheck.mjs +56 -0
- package/tests/api_error_envelope_middleware.mjs +99 -0
- package/tests/api_error_registry.mjs +108 -0
- package/tests/api_pagination_contract.mjs +51 -0
- package/tests/api_path_404.mjs +96 -0
- package/tests/app_pair.mjs +168 -0
- package/tests/architect.mjs +140 -0
- package/tests/art_key.mjs +121 -0
- package/tests/art_stage.mjs +121 -0
- package/tests/asset_version.mjs +94 -0
- package/tests/atlas.mjs +69 -0
- package/tests/audit_deps.mjs +74 -0
- package/tests/audit_middleware.mjs +385 -0
- package/tests/auth_admission_gate.mjs +164 -0
- package/tests/auth_anthropic_identity.mjs +95 -0
- package/tests/auth_duplicate_cookie.mjs +56 -0
- package/tests/auth_error_brand.mjs +77 -0
- package/tests/auth_flow_slice1.mjs +223 -0
- package/tests/auth_inactive_builder.mjs +94 -0
- package/tests/auth_page_gate.mjs +92 -0
- package/tests/auth_session_ttl.mjs +155 -0
- package/tests/autonomous_fit_protected_paths.mjs +115 -0
- package/tests/autonomy_gate.mjs +85 -0
- package/tests/autotile_variants.mjs +256 -0
- package/tests/autotile_xverify.mjs +133 -0
- package/tests/backfill_sessions.mjs +101 -0
- package/tests/backfill_task_rewards.mjs +152 -0
- package/tests/backup_cli.mjs +104 -0
- package/tests/bfg.mjs +327 -0
- package/tests/bfg_write.mjs +423 -0
- package/tests/bongos_box_connect.mjs +89 -0
- package/tests/bongos_dispatch.mjs +74 -0
- package/tests/bongos_downloads.mjs +118 -0
- package/tests/bongos_login.mjs +34 -0
- package/tests/box_access.mjs +186 -0
- package/tests/box_auth_check.mjs +114 -0
- package/tests/box_code_staleness.mjs +116 -0
- package/tests/box_connect_e2e.mjs +196 -0
- package/tests/box_cost.mjs +70 -0
- package/tests/box_dns_repoint.mjs +151 -0
- package/tests/box_host_keys.mjs +102 -0
- package/tests/box_onboard.mjs +317 -0
- package/tests/box_scope_session.mjs +115 -0
- package/tests/box_ship_permissions.mjs +71 -0
- package/tests/box_terminal_ssrf.mjs +90 -0
- package/tests/boxes.mjs +1255 -0
- package/tests/branding.mjs +88 -0
- package/tests/bug_attachments.mjs +189 -0
- package/tests/build_info.mjs +88 -0
- package/tests/builder_needs.mjs +99 -0
- package/tests/cap_newcomer_tasks.mjs +114 -0
- package/tests/cascade-dispatch.mjs +95 -0
- package/tests/cf_tunnel.mjs +459 -0
- package/tests/claim_recall.mjs +150 -0
- package/tests/claims_bind_session.mjs +47 -0
- package/tests/claude_materialize.mjs +148 -0
- package/tests/cli_box_scope.mjs +52 -0
- package/tests/cli_lib_formatters.mjs +87 -0
- package/tests/cli_token_reissue.mjs +147 -0
- package/tests/client_bundle_route.mjs +47 -0
- package/tests/cloud_bongos_pack.mjs +85 -0
- package/tests/conductor.mjs +496 -0
- package/tests/confirm_attribution.mjs +156 -0
- package/tests/conflict_resolve.mjs +462 -0
- package/tests/context_pack.mjs +240 -0
- package/tests/cost.mjs +94 -0
- package/tests/cost_audit.mjs +122 -0
- package/tests/cost_budget_status.mjs +78 -0
- package/tests/cost_reconcile.mjs +102 -0
- package/tests/cost_summary_shape.mjs +94 -0
- package/tests/credit_grant.mjs +376 -0
- package/tests/criterion_progress.mjs +220 -0
- package/tests/criterion_proposal.mjs +109 -0
- package/tests/criterion_ref_parse.mjs +53 -0
- package/tests/currency_label.mjs +119 -0
- package/tests/day_cycle.mjs +197 -0
- package/tests/dependencies_migration.mjs +77 -0
- package/tests/dependencies_polymorphic.mjs +212 -0
- package/tests/deploy_instance.mjs +78 -0
- package/tests/devbox_downloads.mjs +122 -0
- package/tests/diff_derived_signals.mjs +115 -0
- package/tests/discord_approvals.mjs +174 -0
- package/tests/discord_autojoin.mjs +158 -0
- package/tests/discord_bot.mjs +94 -0
- package/tests/discord_bugs.mjs +297 -0
- package/tests/discord_channels.mjs +337 -0
- package/tests/discord_foundation.mjs +76 -0
- package/tests/discord_inbound.mjs +190 -0
- package/tests/discord_message_buffer.mjs +87 -0
- package/tests/discord_oauth.mjs +55 -0
- package/tests/discord_reaction.mjs +170 -0
- package/tests/discord_role_sync.mjs +151 -0
- package/tests/display_name.mjs +123 -0
- package/tests/dns_preflight.mjs +74 -0
- package/tests/event_sound_prefs.mjs +194 -0
- package/tests/eviction_policy.mjs +279 -0
- package/tests/figma_adapter.mjs +199 -0
- package/tests/file_map.mjs +91 -0
- package/tests/first_admin_bootstrap.mjs +40 -0
- package/tests/fitness.mjs +435 -0
- package/tests/fitness_two_root.mjs +104 -0
- package/tests/fixtures/session-506-digest.json +34 -0
- package/tests/gate_approvals.mjs +451 -0
- package/tests/gate_review.mjs +152 -0
- package/tests/git_merge_regen.mjs +151 -0
- package/tests/github_push_ancestry.mjs +119 -0
- package/tests/github_push_land_proof.mjs +127 -0
- package/tests/github_push_merge_driver.mjs +175 -0
- package/tests/goal_abuse_matrix.mjs +187 -0
- package/tests/goal_achievement.mjs +146 -0
- package/tests/goal_conflicts.mjs +305 -0
- package/tests/goal_dependencies_read.mjs +73 -0
- package/tests/goal_member_roles.mjs +420 -0
- package/tests/goal_membership_requests.mjs +332 -0
- package/tests/goal_routes.mjs +203 -0
- package/tests/goal_scope_check.mjs +167 -0
- package/tests/goal_tier.mjs +312 -0
- package/tests/goal_wall_proof.mjs +112 -0
- package/tests/grade_attempts.mjs +73 -0
- package/tests/grade_attribution.mjs +117 -0
- package/tests/grade_diff_hash.mjs +191 -0
- package/tests/grade_server_authoritative.mjs +305 -0
- package/tests/grade_unavailable_display.mjs +66 -0
- package/tests/grader.mjs +1960 -0
- package/tests/grader_bypass.mjs +57 -0
- package/tests/grader_panel.mjs +252 -0
- package/tests/grader_prompt_fencing.mjs +192 -0
- package/tests/helpers.mjs +52 -0
- package/tests/hierarchy-config.mjs +81 -0
- package/tests/init.mjs +463 -0
- package/tests/instance_config.mjs +76 -0
- package/tests/instance_manifest.mjs +110 -0
- package/tests/interaction_prefs.mjs +72 -0
- package/tests/key-card.mjs +110 -0
- package/tests/key-status.mjs +45 -0
- package/tests/land_watch.mjs +115 -0
- package/tests/lifecycle-card.mjs +145 -0
- package/tests/lifecycle_breakdown_view.mjs +236 -0
- package/tests/linkify_refs.mjs +264 -0
- package/tests/live_channel.mjs +128 -0
- package/tests/live_sse.mjs +122 -0
- package/tests/llm-cache-route.mjs +110 -0
- package/tests/llm-cache.mjs +140 -0
- package/tests/llm-pricing.mjs +152 -0
- package/tests/local_preview.mjs +136 -0
- package/tests/main_audit.mjs +137 -0
- package/tests/memory_isolation.mjs +431 -0
- package/tests/memory_map.mjs +107 -0
- package/tests/memory_pull.mjs +429 -0
- package/tests/merge_lease.mjs +100 -0
- package/tests/merge_lock.mjs +312 -0
- package/tests/metrics.mjs +147 -0
- package/tests/migration_allocation.mjs +146 -0
- package/tests/migration_namespace.mjs +185 -0
- package/tests/migrations_fresh_replay_guard.mjs +38 -0
- package/tests/mirror_lineage_fulltree.mjs +45 -0
- package/tests/mirror_lineage_proof.mjs +72 -0
- package/tests/mirror_redact.mjs +193 -0
- package/tests/model_alloc_prefs.mjs +63 -0
- package/tests/module-scope-map.mjs +88 -0
- package/tests/module_api.mjs +110 -0
- package/tests/module_cli.mjs +292 -0
- package/tests/module_contributions.mjs +148 -0
- package/tests/module_loader.mjs +286 -0
- package/tests/module_manifest.mjs +194 -0
- package/tests/module_owned_tests.mjs +105 -0
- package/tests/module_provenance.mjs +34 -0
- package/tests/module_route_rank.mjs +147 -0
- package/tests/module_seams.mjs +68 -0
- package/tests/modules.mjs +156 -0
- package/tests/modules_route.mjs +38 -0
- package/tests/newcomer_floor.mjs +17 -0
- package/tests/newcomer_restock.mjs +80 -0
- package/tests/next_number.mjs +154 -0
- package/tests/oauth_secret.mjs +60 -0
- package/tests/onboard.mjs +70 -0
- package/tests/onboarding_gate.mjs +267 -0
- package/tests/onboarding_observability.mjs +344 -0
- package/tests/onboarding_route_signals.mjs +175 -0
- package/tests/onboarding_state.mjs +346 -0
- package/tests/one_active_claim_per_session.mjs +111 -0
- package/tests/orphaned_claim_ship.mjs +135 -0
- package/tests/override_attribution.mjs +142 -0
- package/tests/override_request_ownership.mjs +235 -0
- package/tests/package_core.mjs +435 -0
- package/tests/parallel-load.mjs +402 -0
- package/tests/path_match.mjs +62 -0
- package/tests/permission_path.mjs +151 -0
- package/tests/platform_boot.mjs +167 -0
- package/tests/pre_compact.mjs +60 -0
- package/tests/preview_server.mjs +127 -0
- package/tests/profile_route.mjs +197 -0
- package/tests/provision.mjs +805 -0
- package/tests/provisioning_github_app_routes.mjs +98 -0
- package/tests/public_mirror_export.mjs +124 -0
- package/tests/public_task_resolver.mjs +112 -0
- package/tests/publish_branch_route.mjs +422 -0
- package/tests/publish_manifest.mjs +184 -0
- package/tests/publish_reconciler.mjs +728 -0
- package/tests/rank_gate.mjs +463 -0
- package/tests/rate_limit_public.mjs +217 -0
- package/tests/read_session_export.mjs +89 -0
- package/tests/recall_cli.mjs +82 -0
- package/tests/recall_hints.mjs +119 -0
- package/tests/recent_shipped_window.mjs +80 -0
- package/tests/regrade_eligibility.mjs +202 -0
- package/tests/render_prefs.mjs +122 -0
- package/tests/repo_map.mjs +92 -0
- package/tests/reward_gate.mjs +284 -0
- package/tests/route_rank_check.mjs +243 -0
- package/tests/route_validation.mjs +490 -0
- package/tests/routine_timers.mjs +72 -0
- package/tests/rubric_fixtures.mjs +140 -0
- package/tests/run_routine.mjs +69 -0
- package/tests/sandbox_stage.mjs +582 -0
- package/tests/scrubber_corpus.mjs +257 -0
- package/tests/search_chunker.mjs +209 -0
- package/tests/search_ingest.mjs +96 -0
- package/tests/search_ingest_db.mjs +88 -0
- package/tests/search_isolation.mjs +204 -0
- package/tests/search_route.mjs +95 -0
- package/tests/secret_box.mjs +189 -0
- package/tests/security_docs.mjs +186 -0
- package/tests/security_route.mjs +108 -0
- package/tests/served_paths_shipped.mjs +118 -0
- package/tests/session-cards.mjs +142 -0
- package/tests/session_506_regression.mjs +69 -0
- package/tests/session_caps.mjs +238 -0
- package/tests/session_cost.mjs +133 -0
- package/tests/session_digest.mjs +73 -0
- package/tests/session_eval.mjs +386 -0
- package/tests/session_index.mjs +105 -0
- package/tests/session_records.mjs +369 -0
- package/tests/session_search.mjs +78 -0
- package/tests/session_start_freshness.mjs +123 -0
- package/tests/setup_smoke.mjs +154 -0
- package/tests/ship_autolinkify.mjs +60 -0
- package/tests/ship_automerge_injection.mjs +174 -0
- package/tests/ship_broadcast_routing.mjs +125 -0
- package/tests/ship_broadcast_server.mjs +175 -0
- package/tests/ship_card.mjs +280 -0
- package/tests/ship_ci_deploy.mjs +374 -0
- package/tests/ship_preflight.mjs +368 -0
- package/tests/ship_premerge.mjs +75 -0
- package/tests/ship_resume.mjs +129 -0
- package/tests/ship_upload_skip.mjs +89 -0
- package/tests/ship_zero_commit.mjs +60 -0
- package/tests/single_host_hall.mjs +98 -0
- package/tests/skill_prefs.mjs +150 -0
- package/tests/smoke_preflight.mjs +66 -0
- package/tests/snapshot_picker.mjs +233 -0
- package/tests/sound_prefs.mjs +198 -0
- package/tests/sprite_facing.mjs +35 -0
- package/tests/status-probe.mjs +89 -0
- package/tests/status_backfill.mjs +94 -0
- package/tests/stop_refcheck.mjs +127 -0
- package/tests/streak.mjs +175 -0
- package/tests/subdomain_auth.mjs +58 -0
- package/tests/task_classifier_vocab.mjs +41 -0
- package/tests/task_create_idempotency.mjs +96 -0
- package/tests/task_goal_link.mjs +93 -0
- package/tests/task_ownership_gate.mjs +125 -0
- package/tests/terrain_picker.mjs +298 -0
- package/tests/touches_overlap.mjs +75 -0
- package/tests/triage_development.mjs +140 -0
- package/tests/turn_in_place.mjs +280 -0
- package/tests/upgrade.mjs +357 -0
- package/tests/verify_task_guard.mjs +64 -0
- package/tests/version_close.mjs +76 -0
- package/tests/versions_route.mjs +93 -0
- package/tests/walkability.mjs +188 -0
- package/tests/wandering_prefs.mjs +176 -0
- package/tests/worktree_disambiguation.mjs +105 -0
- package/tests/worktree_guard.mjs +90 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# ADR 0056 — Version-control the prod deploy script as a reviewable mirror, and retry the post-restart healthcheck
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-13
|
|
4
|
+
**Context:** GDS, task [#1026](https://example.com/builders#/task/1026) (follow-up to [#1025](https://example.com/builders#/task/1025)). Builds on [ADR 0042](<redacted>.md) (ci deploy mode — `deploy-prod.yml` SSHes the droplet's `~/deploy.sh`) and [ADR 0043](<redacted>.md) (the forced-command lock on the deploy key).
|
|
5
|
+
**Status:** Accepted.
|
|
6
|
+
**Track:** `internal` (development system / deploy pipeline).
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
On 2026-06-13, `deploy-prod` run 27468951859 (merge `447a58de`) reported **failure** — `Process completed with exit code 7`, immediately after `==> service: active`. The deploy had actually **succeeded** (live `/version` + external `/healthz` confirmed prod on the new commit). The cause: the in-script healthcheck in the droplet's `~/deploy.sh` was
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
sudo systemctl restart example
|
|
14
|
+
sleep 1
|
|
15
|
+
ACTIVE=$(systemctl is-active example) # systemd "active" ≠ port bound
|
|
16
|
+
...
|
|
17
|
+
CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/healthz)
|
|
18
|
+
[ "$CODE" = "200" ] || exit 1
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`systemctl restart` returns once systemd considers the unit *active*, but the Node process may not have bound `127.0.0.1:3000` yet. A fixed `sleep 1` + a **single** curl raced that bind. When curl couldn't connect it exited 7, and under `set -e` the **command substitution itself** aborted the script — *before* the `==> /healthz:` line even printed. Exit 7 (curl "failed to connect") propagated to the SSH caller, so the Actions step went red.
|
|
22
|
+
|
|
23
|
+
Two compounding issues:
|
|
24
|
+
|
|
25
|
+
1. **A false failure on a healthy deploy.** In `ci` mode ([ADR 0042](<redacted>.md)) `ship.js pollCiDeploy` / `publish-status` reads `conclusion=failure` and **cannot auto-advance `confirmed → shipped`** — every ci ship that hits the race needs a manual `POST /tasks/:id/ship` (had to for [#1025](https://example.com/builders#/task/1025)).
|
|
26
|
+
2. **The authoritative deploy script lived only on the droplet.** Prod's `~/deploy.sh` was hand-maintained at `/home/lars/deploy.sh`, un-versioned and un-reviewed — which is exactly why this race went unnoticed. (Staging's analogue, `scripts/deploy/deploy-staging.sh`, *is* in the repo and installed by `deploy-staging.yml` — but it carried the **same** single-curl bug, just manifesting as a clean `fail` rather than exit 7 because its `if ! curl … | grep` form suppresses `set -e`.)
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
### 1. Retry the post-restart healthcheck with short backoff
|
|
31
|
+
|
|
32
|
+
Poll localhost `/healthz` up to **15× at 1s** before concluding failure (matching the cadence of `ship.js pollCiDeploy`). The load-bearing detail is `… ) || CODE=000`, which keeps a connection failure from tripping `set -e` so a slow bind **retries** instead of dying on the first miss; `--max-time 5` bounds a hang; a real failure still exits 1 with the last 30 journal lines. Applied to **both** the prod script and `scripts/deploy/deploy-staging.sh`.
|
|
33
|
+
|
|
34
|
+
### 2. Mirror the prod deploy script in the repo (but do NOT auto-install it)
|
|
35
|
+
|
|
36
|
+
`scripts/deploy/deploy-prod.sh` is now the **version-controlled source-of-truth mirror** of `/home/lars/deploy.sh` — byte-faithful, so `diff <(ssh droplet cat deploy.sh) scripts/deploy/deploy-prod.sh` is empty. This makes the authoritative deploy logic reviewable + diffable and stops the silent drift that hid this bug.
|
|
37
|
+
|
|
38
|
+
It is **not** auto-scp'd onto the droplet the way `deploy-staging.yml` installs `deploy-staging.sh`, and that asymmetry is deliberate: the prod CI deploy key (`otb-ci-deploy-prod`) is **forced-command-locked** in the droplet's `~/.ssh/authorized_keys` —
|
|
39
|
+
`command="/home/lars/deploy.sh",no-pty,no-agent-forwarding,no-port-forwarding,no-X11-forwarding` ([ADR 0042](<redacted>.md)/[0043](<redacted>.md)). That lock means the CI runner can **only** trigger a deploy, never run `scp` or any other command — a trust-boundary control we will **not** weaken just to auto-sync a script. So prod stays hand-installed by an operator (with a non-forced key) whenever the mirror changes:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
scp scripts/deploy/deploy-prod.sh lars@REDACTED_IP:deploy.sh
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The mirror's header documents this install contract.
|
|
46
|
+
|
|
47
|
+
## Consequences
|
|
48
|
+
|
|
49
|
+
- **A slow service bind no longer fails a successful prod deploy** — ci ships auto-advance `confirmed → shipped` without a manual `POST /tasks/:id/ship`.
|
|
50
|
+
- **The prod deploy script is reviewable in the repo** for the first time; the forced-command control is preserved intact.
|
|
51
|
+
- **One manual step remains:** the mirror is the source of truth but is not auto-installed; an operator must reinstall it on change (loud `diff` makes drift visible). Wiring a safe auto-install for prod (e.g. a tightly-scoped second key, or a server-mediated path) is left as a future improvement — out of scope for this bugfix.
|
|
52
|
+
- `provision-staging.sh` has the same single-curl pattern but is a one-time, human-supervised step (not a per-deploy CI hot path); left unchanged, noted as a possible follow-up.
|
|
53
|
+
|
|
54
|
+
## Verification
|
|
55
|
+
|
|
56
|
+
Faithful copies of the new loop, exercised three ways:
|
|
57
|
+
|
|
58
|
+
- **Dead port:** retries then exits **1** cleanly (no exit 7) — proves the `set -e` safety.
|
|
59
|
+
- **Live prod port (on the droplet):** 200 on attempt 1, exit 0.
|
|
60
|
+
- **Slow bind (port binds mid-loop):** retries through the connection failures, catches 200 on attempt 5, exit 0 — the exact incident class.
|
|
61
|
+
|
|
62
|
+
The fixed script was installed to the droplet (atomic rename; prior version backed up to `~/deploy.sh.bak-task1026`), parses cleanly there, and is byte-identical to the repo mirror.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# ADR 0057 — Per-builder container cost ledger (org-funded → self-fund tracking)
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-15
|
|
4
|
+
**Status:** Accepted
|
|
5
|
+
**Track:** `internal` (dev-system / cost oversight).
|
|
6
|
+
**Context:** GDS-V3, task [#602](https://example.com/builders#/task/602), criterion C-cost. Implements the ledger half of ADR 0031 §5 (cost passthrough). Companion to [ADR 0031](<redacted>.md) (the dev box) and the early-stage billing policy ("tracked, not settled" — both drachmae earnings and container costs are recorded but not actually paid/charged yet).
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
Builders run org-provisioned DigitalOcean dev boxes (ADR 0031). We want every box cost event recorded per builder — visible to the builder on their own profile and to the Archon for oversight — so the org can see what each box costs *before* any real self-fund/charge mechanism exists. No payment integration, no drachmae deduction yet.
|
|
11
|
+
|
|
12
|
+
## Decisions
|
|
13
|
+
|
|
14
|
+
1. **Reuse `cost_log`; no new table, no migration.** The existing `cost_log` table (migrations/003, builder-attributed since 046, idempotent on `(source, source_ref)`) already carries timestamp/amount/description/builder_id — the exact shape the task asks for. Container events are logged with `category='compute'` and distinguished by `source`: `box-lifecycle` (compute hours, already shipped via `recordComputeCost`), `box-spinup`, `box-storage`. Bucketing is by `source`, so a $0 spin-up line stays distinct from compute. This avoids a CHECK-constraint migration (the one real deploy risk) entirely.
|
|
15
|
+
|
|
16
|
+
2. **Accrue at lifecycle transitions — no billing cron.** Compute hours settle at park/deprovision (existing). Spin-up logs at provision + wake. Storage (snapshot-only, charged only while *parked*) settles at wake and deprovision from `parked_at`. All accrual rides the `scripts/gds/box.js` lifecycle ops that already run — no new always-on infra.
|
|
17
|
+
|
|
18
|
+
3. **Spin-up is a $0 event marker by default.** DigitalOcean charges no provision fee — billing starts at the first active hour (captured by compute). We still write a spin-up `cost_log` row (at `$0`, overridable via `BOX_SPINUP_COST_USD`) so the ledger records every create/resume as a timestamped line, per the task's "capture spin-up events" requirement. No fabricated cost.
|
|
19
|
+
|
|
20
|
+
4. **Storage = snapshot rate while parked.** `SNAPSHOT_MONTHLY_USD` (~$0.30, overridable) × parked hours. A running box's disk is already in the droplet hourly rate, so storage is a separate line only for the rare manual-park window (the automated lifecycle deprovisions rather than parks — ADR 0031 / box.js).
|
|
21
|
+
|
|
22
|
+
5. **Two views.** Builder-own: `GET /box/me` returns a `cost` block (`{spinup_usd, compute_usd, storage_usd, total_usd}`, own-scoped, **never** `allowBoxScope`), rendered on the box panel framed as "tracked, not charged." Archon: `GET /boxes/cost-ledger` returns the full ledger + per-builder totals (declared archon-first per the route-rank auditor), surfaced as a "Dev-Box Costs" section on the `/watch` page — cost oversight without a DB query.
|
|
23
|
+
|
|
24
|
+
## Consequences
|
|
25
|
+
|
|
26
|
+
- The "compute" category in the public cost summary now also includes near-$0 spin-up rows; negligible.
|
|
27
|
+
- When a real self-fund/charge mechanism lands (drachmae deduction or DO-account handoff), it reads this ledger — the data is already accruing.
|
|
28
|
+
- Pure cost math (`storageCostUsd`, `summarizeBoxCost`) is unit-tested (`tests/box_cost.mjs`); the DB writers ride the box-lifecycle integration smoke.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# ADR 0057 — Approve new members and first dev boxes from Discord (Archon 👍/👎)
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-15
|
|
4
|
+
**Context:** GDS-V4, task [#1100](https://example.com/builders#/task/1100). Builds on [ADR 0033](<redacted>.md) (the in-process Discord bot as a service principal), [ADR 0037](<redacted>.md) (config-driven channel layout), [ADR 0016](<redacted>.md) (server-enforced permissions / trust boundary), [ADR 0035](<redacted>.md) §2 (the first-box Archon approval gate, [#717](https://example.com/builders#/task/717)), and [ADR 0050](<redacted>.md) (invite-gated admission).
|
|
5
|
+
**Status:** Accepted.
|
|
6
|
+
**Track:** `internal` (development system / onboarding + Discord surface).
|
|
7
|
+
|
|
8
|
+
> **⚠️ Superseded in part by [ADR 0059](<redacted>.md) (task [#1141](https://example.com/builders#/task/1141)).** The **box half** — the `📦-box-approvals` channel and the box-intent 👍/👎 approve/deny wiring — is **removed**: there is no longer a first-box approval gate, so an admitted builder's first box provisions hands-off. The **member half** (`🚪-member-approvals` + the admit/dismiss reaction wiring) is unchanged — member-join admission is still the gate. Read the box-channel parts below as historical.
|
|
9
|
+
|
|
10
|
+
## Problem
|
|
11
|
+
|
|
12
|
+
Two recurring Archon decisions could only be made on the example builders hall ("the Watch", `/builders/watch`):
|
|
13
|
+
|
|
14
|
+
1. **Admit a new builder.** A stranger submits their GitHub username → an `access_requests` row lands at `status='pending'` → an Archon flips it to `'invited'` (or `'dismissed'`) via `PATCH /api/gds/access-requests/:id` (ADR 0050).
|
|
15
|
+
2. **Approve a first dev box.** A newcomer's first paid droplet is held at `box_intents.state='pending_approval'` (ADR 0035 §2) until an Archon flips it to `'pending'` via `POST /api/gds/box/intents/:id/approve`, at which point the control-plane runner provisions it.
|
|
16
|
+
|
|
17
|
+
Both required opening the hall. The owner already lives in Discord (where ship news, ideas, and bugs already flow — ADR 0033). Neither request emitted any notification, so an Archon had to *remember to check* the Watch. The ask: do both approvals from Discord, with a 👍 reaction, in two **separate** channels.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
Add two Archon-only Discord channels and wire a reaction handler to the two existing approve actions. **No new authority** is created — Discord becomes a faster trigger for server actions that already exist and are already Archon-gated.
|
|
22
|
+
|
|
23
|
+
- **Channels** (`docs/discord/channels.json`, reconciled per ADR 0037): `🚪-member-approvals` and `📦-box-approvals`, both `view: archon`. Kept separate as requested.
|
|
24
|
+
- **Outbound (request → channel).** When an `access_request` is created (`POST /access-requests`) the GDS posts a one-line request to `#member-approvals`; when a `box_intent` enters `pending_approval` (`POST /box/ensure`) it posts to `#box-approvals`. Both go through the channel's **incoming webhook** (`scripts/discord/lib.postWebhook`) — the same outbound-only mechanism as ship-news — via `src/bongos/approval-broadcast.js`. Best-effort and fire-and-forget: a Discord hiccup never blocks or fails the request path, and the hall queue remains the source of truth.
|
|
25
|
+
- **Inbound (👍/👎 → action).** The in-process bot's reaction handler (`src/bongos/discord-bot.js` → `src/bongos/discord-approvals.js`) reacts to 👍/👎 in those two channels:
|
|
26
|
+
- `#member-approvals` 👍 → `access_requests` `'invited'`; 👎 → `'dismissed'`.
|
|
27
|
+
- `#box-approvals` 👍 → `boxes.approveIntent` (held → `pending`, runner provisions); 👎 → `boxes.denyIntent`.
|
|
28
|
+
|
|
29
|
+
### Marker contract
|
|
30
|
+
|
|
31
|
+
Each request line **leads** with a `<kind> #<id>` token (`member-request #<id>` / `box-intent #<id>`), mirroring how ship-feed lines lead with `#<taskId>` (ADR 0033 §3). The handler anchors to the start of the message, so a body that happens to contain another `#5` cannot hijack the mapping. The producer (`approval-broadcast`) and parser (`discord-approvals.parseApprovalMarker`) are unit-tested to round-trip.
|
|
32
|
+
|
|
33
|
+
### The deny path for boxes
|
|
34
|
+
|
|
35
|
+
`box_intents.state` has no `denied` value, and adding one would need a migration to widen the CHECK. A 👎 instead resolves the held intent to terminal `state='error'` with `last_error='denied:archon:<login>'` (free-text `box_events.event='intent_denied'` records it). The runner only ever claims `state='pending'`, so a denied intent is never provisioned; `'error'` reads distinctly from a real provisioning failure via the `denied:` prefix. A terminal state frees the `ux_box_intents_open` index, so a declined newcomer can re-request later — symmetric with how a `'dismissed'` access request can be re-submitted. **No migration.**
|
|
36
|
+
|
|
37
|
+
## Why this is safe — the trust boundary holds (ADR 0016 / 0033 §1)
|
|
38
|
+
|
|
39
|
+
The one invariant: *every bot-triggered write re-checks the linked builder's LIVE rank.* The bot asserts only **who** reacted (Discord user → linked builder via the F1 link table, which reads `builders.rank` fresh); the GDS decides **what** they may do. Both actions are `requireRank('archon')` on the web, so `discord-approvals` re-derives the reactor's live rank and acts **only** when it is `archon`. A Metic, a player, or an unlinked reactor is a silent no-op. The bot cannot escalate; the worst a compromised bot could do is mis-report which Archon reacted — bounded by the `audit_log` (every write recorded with `surface='discord_bot'`).
|
|
40
|
+
|
|
41
|
+
Defense in depth, all fail-closed:
|
|
42
|
+
|
|
43
|
+
- **Webhook-only request lines.** The handler acts only on messages with a non-null `webhookId`. A member cannot post as a webhook (needs Manage Webhooks + the secret URL), so a hand-typed `member-request #5` can't trigger an approval — enforced in code, not just by channel permissions (the same guard ADR 0033 §3 uses for upvotes).
|
|
44
|
+
- **Channel-pinned.** The handler is inert unless the channel ids are configured (`DISCORD_MEMBER_APPROVALS_CHANNEL_ID` / `DISCORD_BOX_APPROVALS_CHANNEL_ID`, or the creds file) — it never acts on reactions in arbitrary channels.
|
|
45
|
+
- **Idempotent.** Both actions guard on the current DB state (`status='pending'` / `state='pending_approval'`), so a duplicate or late reaction matches nothing and is a no-op (`already_resolved`), never a double-write.
|
|
46
|
+
- **Mention pings suppressed.** `postWebhook` already sends `allowed_mentions:{parse:[]}` ([#871](https://example.com/builders#/task/871)), so an attacker-influenceable display name / note in a request line can't `@everyone` the server.
|
|
47
|
+
- **Never throws into the gateway.** Every handler path is caught; the bot can't be crashed by a malformed reaction, and the web/game process is never at risk (ADR 0033 fail-safe).
|
|
48
|
+
|
|
49
|
+
## Operator setup (inert until configured)
|
|
50
|
+
|
|
51
|
+
The feature ships dormant — the same posture as every other Discord surface. To turn it on:
|
|
52
|
+
|
|
53
|
+
1. Create the `🚪-member-approvals` and `📦-box-approvals` channels (or let the channel reconciler create them from `channels.json`).
|
|
54
|
+
2. Create an incoming webhook in each; store the URLs as `member_approvals` / `box_approvals` in `~/.config/otb/discord-webhooks.json` (or env `DISCORD_MEMBER_APPROVALS_WEBHOOK` / `DISCORD_BOX_APPROVALS_WEBHOOK`).
|
|
55
|
+
3. Record the two channel ids as `member_approvals_channel_id` / `box_approvals_channel_id` in `~/.config/otb/discord-bot.json` (or the matching env vars).
|
|
56
|
+
|
|
57
|
+
Until both halves are set, requests simply aren't posted and reactions aren't acted on; the hall Watch is unaffected.
|
|
58
|
+
|
|
59
|
+
## Alternatives considered
|
|
60
|
+
|
|
61
|
+
- **A new HTTP endpoint the bot calls.** Rejected — the bot is in-process (ADR 0033 F3), so it calls the function directly: no localhost self-HTTP, no minted bot session token to store/leak, strictly less attack surface. Same rationale as `discord-reaction`.
|
|
62
|
+
- **Reading the reactor's Discord @Archon role to authorize.** Rejected — Discord roles are cosmetic badges synced one-way from `builders.rank` (ADR 0033 §4); trusting them back would invert the trust boundary. We re-derive rank from the GDS.
|
|
63
|
+
- **Bot posts confirmations via the gateway client.** We post the short `✅ … invited/approved.` confirmation through the channel webhook instead, keeping the bot's write surface to Discord minimal (it adds no authority either way).
|
|
64
|
+
- **A migration adding a `denied` box-intent state.** Rejected for now — terminal `error` + a `denied:` prefix is sufficient and avoids a schema change; revisit if the roster UI needs to distinguish denials visually.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# ADR 0058 — Automatic gate-surface gatekeeper (rank-based auto-approval + deterministic hard floor)
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-06-15
|
|
5
|
+
- **Track:** internal
|
|
6
|
+
- **Builds on:** [ADR 0042](<redacted>.md) (CI self-deploy), [ADR 0043](<redacted>.md) (protected paths + rank floor), [ADR 0016](<redacted>.md) (trust boundary), [ADR 0055](<redacted>.md) (server-mediated publish), [ADR 0057](<redacted>.md) (Archon approval channels)
|
|
7
|
+
- **Task:** 1139
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
In deploy mode `ci` ([ADR 0042](<redacted>.md)) PRs auto-merge on green required checks and `main` auto-deploys to prod. `CODEOWNERS` (owner review on gate surfaces, enforced by `require_code_owner_reviews`) was the **only** human in that loop — it stops a builder from weakening the gate from inside their own auto-merging PR (disabling a check, muting a test, lowering a rank requirement, sneaking a malicious migration).
|
|
12
|
+
|
|
13
|
+
That review made the owner a bottleneck: **every** PR touching a gate surface (the ~36 `PROTECTED_GLOBS` in [`src/bongos/permission-path-check.js`](../../src/bongos/permission-path-check.js)) blocked on a manual approval — and nothing surfaced those pending PRs, so server-mediated-publish PRs ([ADR 0055](<redacted>.md)) stranded silently (the 2026-06-15 retro: tasks 1068/1096/602/476 sat `confirmed` for days, blocked on an approval the owner never saw).
|
|
14
|
+
|
|
15
|
+
A red-team design pass established one hard truth: a **deterministic** analyzer can only *safely* certify a tiny set of changes as benign (un-muting a test, narrowing the secret allowlist, and PRs touching no gate surface). New migrations, dependency bumps, and auth/route changes are **undecidable** by rules — and an LLM reviewer is rejected as authoritative because the project's own history (grader prompt-injection fencing, task 995) proves it is trickable.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
A **rank-based** gatekeeper (owner directive, 2026-06-15: "I don't want to review anything manually"), implemented as a deterministic CI check with three verdicts over the PR **diff-as-data**:
|
|
20
|
+
|
|
21
|
+
- **PASS** — no gate surface, or only **AUTO** pure-removal surfaces (`run-unit-tests.js` removing a test-mute; `.husky/secrets-allowlist.txt` removing a line). Auto-merge. This already clears the *common* PR (game/art/docs/non-gate `src/`).
|
|
22
|
+
- **NEEDS_TRUST** — a sensitive-but-routine gate surface (migrations, deps, routes, `server.js`, ship plumbing, boxes, doctrine docs, a `db.js` change outside the rank-tier region, a deleted/shrunk test). Auto-merges **iff the author is Metic+** (rank trust, server-resolved) **or the owner approved**; otherwise escalates.
|
|
23
|
+
- **ESCALATE — the HARD FLOOR (rank-independent)** — the gate's own machinery + deploy switch: `gate-review.js` itself, `.github/`, `CODEOWNERS`, `config/deploy.json`, `infra/` (branch-protection-as-code), `auth.js`, `route-rank-check.js`, `permission-path-check.js`, the grader, `main-audit.js`, `run-unit-tests.js` (non-narrowing), the hooks, and `db.js`'s `REQUIRES_RANK_TIER` (content rule). **No rank bypasses this** — it always pings the owner. Pre-flight rejections (symlink, non-ASCII/whitespace path, oversize diff, analyzer error) also ESCALATE, fail-closed.
|
|
24
|
+
|
|
25
|
+
The hard floor is the irreducible root of trust: a compromised or prompt-injected senior session **cannot** auto-merge a change that disables the gatekeeper, the deploy-time audit, branch protection, or the rank brain. The owner explicitly accepted the residual risk that a hijacked senior session could auto-merge a *non-floor* sensitive change — bounded by (a) this floor, (b) the existing autonomous-path protected-path exclusion (task 994), and (c) the deploy-time `main-audit.js --verify-head` backstop.
|
|
26
|
+
|
|
27
|
+
### Why these specifics (red-team-closed)
|
|
28
|
+
|
|
29
|
+
- **Gate-surface set = imported `PROTECTED_GLOBS`, never a hand-typed list** (red-team finding `#1`: a hand-typed shrink dropped `db.js`/`routes.js`/`server.js` and was *less* safe than today). A drift test asserts no protected glob is ever classified PASS.
|
|
30
|
+
- **Two-layer enforcement**: the in-CI `gate-review` check is the fast cooperative gate; `main-audit.js --verify-head` on the droplet is the authoritative net (asserts the merged SHA carries a `success` gate-review conclusion — closes the workflow-self-neuter, raw-push, and cancelled-check races).
|
|
31
|
+
- **Trusted-base overlay**: the workflow overlays the whole `scripts/` + `src/` from `origin/main` before running, so a PR cannot edit the analyzer to pass itself.
|
|
32
|
+
- **`run-unit-tests.js`, `config/deploy.json`, `CODEOWNERS`, `scripts/deploy/`, `gate-review.js`, `grader-rubric.json` added to `PROTECTED_GLOBS`** — these gate-machinery paths were previously unprotected at the sub-Metic floor; adding them closes that pre-existing gap and makes the hard floor a subset of the protected set.
|
|
33
|
+
- **Escalation reuses [ADR 0057](<redacted>.md)**: a `#gate-approvals` channel + `gate-pr #<n>` marker; the owner clears it by approving the (App-authored) PR. Inert until `DISCORD_GATE_APPROVALS_WEBHOOK` is set.
|
|
34
|
+
|
|
35
|
+
## Rollout (the gate is never weaker mid-flight)
|
|
36
|
+
|
|
37
|
+
1. **Report-only** (this task): ship `gate-review.js` + `gate-review.yml` with `GATE_REPORT_ONLY=1` (always exit 0 — verdict to the run summary only). `CODEOWNERS`/branch protection unchanged → gate identical to today.
|
|
38
|
+
2. **Verify**: confirm the verdict against real PRs + the red-team matrix (`tests/gate_review.mjs`).
|
|
39
|
+
3. **Enforce**: drop `GATE_REPORT_ONLY`, add `gate-review` to required checks, add the `--verify-head` conclusion assertion, and wire the server rank-attestation (`GATE_AUTHOR_TRUSTED`) + the `#gate-approvals` ping. Then shrink `CODEOWNERS` only for the two AUTO surfaces.
|
|
40
|
+
|
|
41
|
+
## Consequences
|
|
42
|
+
|
|
43
|
+
- The owner's manual-review surface shrinks from "every PR touching ~36 protected paths" to "only changes to the gate's own machinery + deploy switch" — pinged, never silently blocked.
|
|
44
|
+
- A purely-deterministic gate could not deliver "review nothing" without the rank-trust layer (migrations/deps are undecidable); the rank layer is the deliberate trade the owner chose.
|
|
45
|
+
- The hard floor + two-layer enforcement keep the safety system itself un-auto-disablable.
|
|
46
|
+
|
|
47
|
+
## Update — enforcing, and the two server-attestation vectors (task 1142, task 1169)
|
|
48
|
+
|
|
49
|
+
The gatekeeper went **ENFORCING** (task 1142): `gate-review.yml` no longer sets `GATE_REPORT_ONLY`, and `gate-review` is a required check. The `GATE_AUTHOR_TRUSTED` signal is the `gate-author-trust` **commit status** — set only by the server (which alone holds the authenticated builder's server-resolved rank + the App/PAT credential), so CI reads it but cannot forge it. The server posts it via `githubPush.setCommitStatus` from **two** ship paths, sharing one helper (`postGateAuthorTrust` in [`src/bongos/routes/tasks.js`](../../src/bongos/routes/tasks.js)):
|
|
50
|
+
|
|
51
|
+
1. **Server-mediated publish** ([ADR 0055](<redacted>.md), dev box / `pushVia 'server'`): `POST /tasks/:id/publish-branch` attests the pushed tip after opening the PR.
|
|
52
|
+
2. **Laptop `ci`-land** (`pushVia 'local'` — task 1169): `ship.js` pushes the branch + opens the PR with the laptop's own `gh` credential, which **cannot** forge the attestation. So `ciLandLocal` calls `POST /tasks/:id/attest-gate` (`requireBuilder` + ownership, deliberately **no** `allowBoxScope`) right **after the push and before opening the PR** — race-free for a fresh PR, since the PR-open is what fires `gate-review` and it runs only after the status is already present. Before task 1169 the laptop path posted *nothing*, so a Metic+ author's gate-surface PR from a laptop was BLOCKED and they could not self-approve their own PR (hit live on task 1162 / PR #131).
|
|
53
|
+
|
|
54
|
+
A commit status posted on a SHA does **not** re-trigger a `pull_request`-triggered check, so a **reused** open PR that already ran `gate-review` red (on the same SHA, before the attestation existed) gets a best-effort `gh run rerun` nudge of that stale run. A freshly-opened PR needs no nudge.
|
|
55
|
+
|
|
56
|
+
## Update — owner approval from the hall (task 1179)
|
|
57
|
+
|
|
58
|
+
Clearing an ESCALATE (and a NEEDS_TRUST without a Metic+ author) required a GitHub `APPROVED` review by `example-owner` on the head SHA — i.e. the owner had to leave a GitHub/terminal review, which the non-technical owner should never have to (feedback: auth flows must be UI-first). This adds a **second owner-approval channel**, trust-equal to the GitHub review, drivable from the builders' hall:
|
|
59
|
+
|
|
60
|
+
- **The Gate** (`/builders/gate`, Archon-only page, server-gated by `requireArchonPage`) lists every open PR whose `gate-review` check is red — the LIVE check conclusion is the authoritative "awaiting approval" signal — and renders **why** by reusing the **exact** pure `classifyChanges()` the CI runs (no duplicated policy). Each held PR shows its task, author, head SHA, the verdict + per-file reasons (hard-floor flagged), changed files, and a diff link.
|
|
61
|
+
- **Approve** (`POST /api/gds/gate-approvals/:pr/approve`, `requireBuilder` + `requireRank('archon')`) sets a **`gate-owner-approved`** commit status on the head SHA via `githubPush.setGateOwnerApproved` — set **exactly like** `gate-author-trust`: only the server holds the credential, and it sets it only after re-checking the caller's Archon rank server-side ([ADR 0016](<redacted>.md)). A PR author cannot forge it. `gate-review.yml`'s trust step now resolves `OWNER_APPROVED=1` from **either** a `example-owner` GitHub review **or** this status. Both are read on the head SHA, so a later commit's head carries no approval (the same red-team `#2b` sha-pin protection).
|
|
62
|
+
- **Re-trigger without a new App permission.** A commit status does not re-fire a `pull_request` check, and the publish App is not granted `actions:write`. So Approve **closes+reopens** the PR (`recheckPullRequest`, using the `pull_requests:write` the App already holds), which fires `pull_request: reopened` → `gate-review` re-runs with the real PR context, reads the new status on the correct head SHA, and goes green; auto-merge is re-armed and the publish reconciler `mergeIfGreen` sweep (task 1162) is the backstop.
|
|
63
|
+
|
|
64
|
+
The approve route **clears the hard floor**, so `POST /gate-approvals/:pr/approve` is pinned `archon` in `route-rank-check.EXPECTED_RANKS` (a silent downgrade would let any builder clear an ESCALATE). Because `gate-review.yml` is itself a hard-floor surface, the PR that introduces this change ESCALATEs and must be owner-approved to land — the bootstrapping case, shipped via `OTB_PUSH_VIA_SERVER=1` so the App authors the PR and the owner can approve it. Coverage: `tests/gate_approvals.mjs`. Authority is unchanged in substance — only `rank='archon'` (server-checked) can clear the gate, as before; this just adds a browser surface to do it.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# ADR 0059 — Single approval: remove the dev-box (first-box) Archon approval gate
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-06-16
|
|
5
|
+
- **Track:** internal
|
|
6
|
+
- **Supersedes:** [ADR 0035](<redacted>.md) §2 (the first-box Archon approval gate, [#717](https://example.com/builders#/task/717) / migration 071) and the **box half** of [ADR 0057](<redacted>.md) (the `#box-approvals` channel + the box-intent approve/deny wiring)
|
|
7
|
+
- **Builds on:** [ADR 0050](<redacted>.md) (invite-gated admission), [ADR 0031](<redacted>.md) (cloud dev boxes + the box provisioning rank floor), [ADR 0016](<redacted>.md) (server-enforced permissions)
|
|
8
|
+
- **Task:** 1141
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
A builder hit **two** human gates between "I heard about this" and "I'm shipping from a dev box":
|
|
13
|
+
|
|
14
|
+
1. **Member-join admission** ([ADR 0050](<redacted>.md)). A stranger submits their GitHub username → an `access_requests` row lands `pending` → an Archon invites or dismisses it (the hall Watch, or the `#member-approvals` Discord 👍/👎 from [ADR 0057](<redacted>.md)). This is the **admission** gate: it decides who is allowed into the hall at all.
|
|
15
|
+
2. **First-box Archon approval** ([ADR 0035](<redacted>.md) §2, [#717](https://example.com/builders#/task/717), migration 071). An already-admitted builder's *first* dev box was held at `box_intents.state='pending_approval'` until an Archon approved it (`POST /api/gds/box/intents/:id/approve`, or the `#box-approvals` Discord 👍/👎). Only then did the control-plane runner provision the paid (~$24/mo) droplet.
|
|
16
|
+
|
|
17
|
+
The first-box gate existed when admission was **open** — back then anyone with a GitHub account could become a Xenos with zero human in the loop, so a second human gate on the first paid box was the only thing standing between a signup and a charge ([ADR 0035](<redacted>.md) §Problem-3). Since [ADR 0050](<redacted>.md) closed that hole — admission is now invite-gated by default — the two gates are **redundant**: an Archon already had to admit this person before they could request a box at all. The second approval is a now-pointless second click that an Archon must remember to action (or it strands silently — the same failure mode the 2026-06-15 gate-surface retro called out for stranded approvals).
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
Collapse the two human gates to **one — member-join admission.** Once a member is admitted ([ADR 0050](<redacted>.md)), their first dev box provisions **hands-off** through the existing box-intent runner: `POST /api/gds/box/ensure` enqueues the intent at `state='pending'` straight away (the same state a wake or a subsequent re-provision already uses), and the control-plane runner drains it unchanged. There is no `pending_approval` hold and no first-box approve step.
|
|
22
|
+
|
|
23
|
+
This **supersedes** [ADR 0035](<redacted>.md) §2 (Decision §2, "A newcomer's FIRST box requires Archon approval") and the **box half** of [ADR 0057](<redacted>.md) (the `#box-approvals` channel and its box-intent approve/deny reaction wiring). The rest of [ADR 0035](<redacted>.md) (§1 Metic+ local clone, §3 the single hosted guide) and the **member half** of [ADR 0057](<redacted>.md) (the `#member-approvals` channel) stand unchanged.
|
|
24
|
+
|
|
25
|
+
## Consequences
|
|
26
|
+
|
|
27
|
+
- **The member invite becomes the SOLE human gate before a paid (~$24/mo) box is created.** An admitted Xenos can provision their first box hands-off — the spend is bounded by the fact that a human already chose to admit them, not by a second per-box click.
|
|
28
|
+
- **The box provisioning RANK FLOOR still bounds eligibility.** `BOX_PROVISION_MIN_RANK` ([`src/bongos/boxes.js`](../../src/bongos/boxes.js)) is unchanged: the floor is still the rank-side control on who may have a box at all, and the per-builder "one box at a time" invariant (`box_intents` partial unique index, one `builder_boxes` row) is untouched. Removing the *first-box* gate does not widen *who* qualifies; it only removes the second human click for someone already admitted and already above the floor.
|
|
29
|
+
- **Migration 102 reverts the schema** introduced by migration 071 — the `pending_approval` box-intent state (and any approval-tracking column) is removed; in-flight first-box intents are migrated to `pending` so the runner drains them.
|
|
30
|
+
- **The box-panel "awaiting approval" UI state is removed** — `/builder-box` and the hall box panel no longer surface "your box is awaiting approval," because no box waits.
|
|
31
|
+
- **The `#box-approvals` Discord channel + its box-intent approve/deny wiring are removed** (the box half of [ADR 0057](<redacted>.md)). `#member-approvals` and the admission approve/dismiss wiring stay — admission is still the gate.
|
|
32
|
+
- **The trust-boundary invariant from [ADR 0031](<redacted>.md) §6 is unchanged.** The web tier still only *records a provision intent*; it never holds `DO_API_TOKEN` and never provisions directly. This ADR removes a human approval step, not the control-plane separation.
|
|
33
|
+
|
|
34
|
+
## Alternatives considered
|
|
35
|
+
|
|
36
|
+
- **Keep both gates.** Rejected — with admission now invite-gated ([ADR 0050](<redacted>.md)), the first-box gate is a redundant second click on a person an Archon already vetted, and it strands silently when un-actioned. The cost concern it was built for ([ADR 0035](<redacted>.md) §Problem-3) is already covered by admission + the rank floor.
|
|
37
|
+
- **Drop admission and keep first-box approval instead.** Rejected — admission is the stronger control (it gates *all* access, including reading and idea-filing, not just the box), and [ADR 0050](<redacted>.md) deliberately made it the default. Keeping the weaker, narrower gate while dropping the broader one inverts the design.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# ADR 0060 — GDS retrieval layer: Postgres-resident, lexical-first, rank-scoped, fail-open
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-06-16
|
|
5
|
+
- **Track:** internal
|
|
6
|
+
- **Builds on:** [ADR 0016](<redacted>.md) (server-enforced permissions — the read posture this index must never bypass), [ADR 0043](<redacted>.md) (sub-Metic stay out of the permission/methodology core), [ADR 0024](<redacted>.md) (the memory/learning corpus this indexes), [ADR 0010](0010-pms-architecture.md) (the GDS Postgres this lives in)
|
|
7
|
+
- **Task:** 940 (V4.R02) — locks the architecture for the GDS-V4 "one-call recall" criterion. Implemented by V4.R06 (migration, [#944](https://example.com/builders#/task/944)), V4.R07 (chunker, [#945](https://example.com/builders#/task/945)), V4.R09/R10 (ingesters, [#947](https://example.com/builders#/task/947)/[#948](https://example.com/builders#/task/948)), V4.R11 (search route, [#949](https://example.com/builders#/task/949)), V4.R12 (scoping + isolation suite, [#950](https://example.com/builders#/task/950)); the embedding layer is gated behind the V4.R24 eval ([#962](https://example.com/builders#/task/962)).
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
A builder starting a session re-discovers the same project knowledge by hand every time: which ADR covers X, which session log hit this footgun, which task already shipped this. The 2026-06-12 friction analysis of the BFG session corpus put "rediscovery / wrong-path" at the top of the time sinks. The GDS-V4 headline is **one-call recall**: any session, from any machine, can ask "what do we know about X?" and get the relevant docs + DB prose back in one call — *without* the recall layer ever becoming a side-channel that leaks content a builder isn't allowed to read.
|
|
12
|
+
|
|
13
|
+
Two hard constraints frame every choice below:
|
|
14
|
+
|
|
15
|
+
1. **Budget + ops (CLAUDE.md §4).** $5k/year, one ~$14/mo droplet, one Node process, one Postgres. A retrieval stack that adds a managed vector DB, a separate search service, or a GPU box is off the table unless lexical-in-Postgres demonstrably can't do the job.
|
|
16
|
+
2. **The trust boundary ([ADR 0016](<redacted>.md) / [ADR 0043](<redacted>.md)).** Some indexed content is rank-gated (permission-core docs are Metic+; a builder's own memory is theirs alone). A search index is a classic read-side bypass: if it returns a snippet of a doc the caller can't otherwise read, the index just defeated the boundary. Scoping is therefore a **first-class, server-enforced** property of the index, not a filter bolted on later.
|
|
17
|
+
|
|
18
|
+
The 2026-06-12 research report ("GDS retrieval architecture") surveyed the options and recommended **lexical-first, in Postgres**: Postgres ships full-text search (`tsvector`/`tsquery`) and trigram fuzzy match (`pg_trgm`) in the box we already run; the corpus (a few thousand markdown chunks + DB prose rows) is tiny by search standards; and embeddings buy little until a *measured* lexical gap justifies their cost and operational weight. This ADR locks that in.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
### 1. Substrate — one table in the existing Postgres, no new service
|
|
23
|
+
|
|
24
|
+
All searchable content is chunked into one table, `search_chunks`, in the GDS Postgres. No new process, no managed service, no network hop. Full-text and fuzzy match run as SQL against this table.
|
|
25
|
+
|
|
26
|
+
### 2. Schema (`search_chunks`, migration 103 / [#944](https://example.com/builders#/task/944))
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
search_chunks
|
|
30
|
+
id bigserial primary key
|
|
31
|
+
source_kind text not null -- 'doc' | 'db' (markdown file vs DB-prose row)
|
|
32
|
+
source_ref text not null -- e.g. 'docs/adr/0016-....md' or 'task:431' / 'learning:53'
|
|
33
|
+
chunk_index int not null -- ordinal within the source (0-based)
|
|
34
|
+
heading_path text -- breadcrumb: 'CLAUDE.md > §8 Versioning > Working rules'
|
|
35
|
+
title text -- the source's human title (for result display)
|
|
36
|
+
body text not null -- the chunk text (what gets shown as a snippet)
|
|
37
|
+
min_rank text not null default 'xenos' -- LOWEST rank that may see this chunk (scoping, §6)
|
|
38
|
+
owner_builder_id bigint references builders(id) -- non-null ⟺ a per-builder private chunk (e.g. memory); else NULL = shared
|
|
39
|
+
content_hash text not null -- sha256 of body; ingestion delta key (skip unchanged)
|
|
40
|
+
tsv tsvector -- generated/maintained FTS vector over title+heading_path+body
|
|
41
|
+
created_at timestamptz not null default now()
|
|
42
|
+
updated_at timestamptz not null default now()
|
|
43
|
+
unique (source_kind, source_ref, chunk_index)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Indexes: a **GIN** index on `tsv` (FTS) and a **GIN `gin_trgm_ops`** index on `body` (trigram fuzzy). `pg_trgm` is enabled via `CREATE EXTENSION IF NOT EXISTS pg_trgm` in the same migration (ships with standard Postgres contrib; no new package). A btree on `(source_kind, source_ref)` supports idempotent re-ingest + delete-by-source.
|
|
47
|
+
|
|
48
|
+
### 3. Chunking — heading-aware with breadcrumbs (V4.R07 / [#945](https://example.com/builders#/task/945))
|
|
49
|
+
|
|
50
|
+
Markdown is split on heading boundaries (not fixed byte windows), so a chunk is a coherent section. Each chunk carries a `heading_path` breadcrumb (`File > H1 > H2 > H3`) so a result is self-locating and the snippet has context. Front-matter is parsed (not indexed as body). Over-long sections are sub-split on paragraph boundaries with the breadcrumb preserved. The chunker is a **pure module** (`src/bongos/search-chunker.js`) so both ingesters and the test suite drive it without I/O.
|
|
51
|
+
|
|
52
|
+
### 4. Ingestion — deploy hook (repo) + nightly timer (DB prose)
|
|
53
|
+
|
|
54
|
+
- **Repo markdown** (V4.R09 / [#947](https://example.com/builders#/task/947)): a deploy-hook step (`scripts/gds/search-ingest-md.js`) walks the repo's docs/markdown, chunks each file, and upserts. Delta by `content_hash` — unchanged chunks are skipped; vanished sources are deleted. Runs on every deploy so the index tracks `main`.
|
|
55
|
+
- **DB prose** (V4.R10 / [#948](https://example.com/builders#/task/948)): a nightly timer (`scripts/gds/search-ingest-db.js`) ingests prose-bearing DB rows — task descriptions/value summaries, learnings, session-log summaries, recent shipped-task summaries — with a recency boost. Nightly is fresh enough for DB prose; the deploy hook keeps docs current intra-day.
|
|
56
|
+
|
|
57
|
+
Each ingested chunk is stamped with the `min_rank` of its source (§6).
|
|
58
|
+
|
|
59
|
+
### 5. Query — lexical hybrid fused with RRF (V4.R11 / [#949](https://example.com/builders#/task/949))
|
|
60
|
+
|
|
61
|
+
`POST /api/gds/search { q, limit? }` runs **two** retrievers and fuses them:
|
|
62
|
+
|
|
63
|
+
1. **FTS leg** — `websearch_to_tsquery` against `tsv`, ranked by `ts_rank_cd`.
|
|
64
|
+
2. **Trigram leg** — `body % q` / `similarity(body, q)` via `pg_trgm`, which catches typos, partial tokens, and identifier fragments FTS stems away.
|
|
65
|
+
|
|
66
|
+
Results are fused with **Reciprocal Rank Fusion** (RRF: `score = Σ 1/(k + rank_i)`, k=60) — rank-based, so the two legs' incomparable score scales don't need normalizing, and a chunk ranked well by *either* leg surfaces. RRF is computed in SQL (a CTE per leg + a fused aggregate) so there's one round-trip. The response returns `{ source_kind, source_ref, heading_path, title, snippet, score }[]`.
|
|
67
|
+
|
|
68
|
+
### 6. Scoping — per-chunk `min_rank` + owner, server-enforced (V4.R12 / [#950](https://example.com/builders#/task/950))
|
|
69
|
+
|
|
70
|
+
This is the load-bearing security property, mirroring [`src/bongos/routes/memory.js`](../../src/bongos/routes/memory.js)'s posture:
|
|
71
|
+
|
|
72
|
+
- Every query is filtered **in the SQL WHERE clause** (not post-hoc in JS) by the caller's **live, server-resolved rank** (`req.builder.rank`, read fresh per request — never from input): `min_rank` must be ≤ the caller's rank in the ladder.
|
|
73
|
+
- A chunk with a non-null `owner_builder_id` (a per-builder private chunk, e.g. memory) is returned **only** to that owner — `owner_builder_id IS NULL OR owner_builder_id = req.builder.id`. There is no parameter to name another builder; the owner is always the authenticated session.
|
|
74
|
+
- Permission-core docs (the [ADR 0043](<redacted>.md) protected set: trust-boundary docs, rank/authz, the ship/grade/deploy pipeline) are ingested at `min_rank='metic'` so a Xenos/Thetes search can never return a snippet of them.
|
|
75
|
+
- **No public/unauthenticated surface.** `POST /api/gds/search` is `requireBuilder`. The public read API ([`src/bongos/routes/public.js`](../../src/bongos/routes/public.js)) gains nothing here.
|
|
76
|
+
- A dedicated isolation suite (`tests/search_isolation.mjs`, in the style of `tests/memory_isolation.mjs`) proves: a sub-Metic query never returns a `min_rank='metic'` chunk; a builder never returns another builder's owned chunk; the filter is in SQL, not a JS post-filter that a future refactor could drop.
|
|
77
|
+
|
|
78
|
+
### 7. Fail-open to lexical-only
|
|
79
|
+
|
|
80
|
+
The (future, V4.R24-gated) embedding/semantic leg is **additive and fail-open**: if embeddings are absent, disabled, or error, search degrades to the lexical legs above — it never hard-fails a query. Lexical-first means the system is fully useful with zero embedding infrastructure; embeddings are a later, measured upgrade, not a dependency.
|
|
81
|
+
|
|
82
|
+
## Consequences
|
|
83
|
+
|
|
84
|
+
- **Zero new infra / monthly cost.** Search is SQL against the existing Postgres. No vector DB, no search service, no GPU.
|
|
85
|
+
- **The index can never be a read-side bypass.** Scoping is enforced server-side in SQL against the live rank + authenticated owner, identical in spirit to the memory router. The isolation suite is the regression guard.
|
|
86
|
+
- **Lexical is the floor, embeddings are the option.** We ship a useful recall layer now; the embedding go/no-go is a *measured* decision (V4.R24 [#962](https://example.com/builders#/task/962)) against a golden eval set (V4.R01 [#939](https://example.com/builders#/task/939)), and even if adopted it fails open to this.
|
|
87
|
+
- **Freshness is bounded + visible.** Docs track `main` on deploy; DB prose is nightly. Staleness monitoring + a `/watch` tile (V4.R36 [#972](https://example.com/builders#/task/972)) surface a stalled ingest rather than letting it rot silently.
|
|
88
|
+
- **`search_chunks` fattens the nightly `pg_dump`.** Reviewed under V4.R37 ([#973](https://example.com/builders#/task/973)); chunks are derivable from `main` + DB, so the backup could exclude them if size warrants.
|
|
89
|
+
|
|
90
|
+
## Alternatives considered (rejected)
|
|
91
|
+
|
|
92
|
+
- **ParadeDB / `pg_search` (BM25 in Postgres).** A real BM25 index would beat `ts_rank`, but it's a non-core extension that must be compiled/installed on the droplet — operational weight and a deploy-time dependency for a corpus small enough that `ts_rank_cd` + trigram + RRF is plenty. Rejected on ops cost vs marginal quality. Revisit only if the golden-set eval shows lexical ranking is the bottleneck.
|
|
93
|
+
- **Standalone vector DB (Qdrant / Weaviate / Pinecone).** A separate always-on service (or per-query SaaS spend) for a few-thousand-chunk corpus — squarely against the budget posture, and it splits the source of truth out of Postgres. Rejected.
|
|
94
|
+
- **Embeddings now (pgvector from day one).** Embeddings cost API spend per ingest + per query and add a provider dependency, for unproven recall gain on a small, jargon-dense corpus where exact-term + trigram match is strong. Deferred behind a measured gate (V4.R24), and even then layered on (fail-open), not depended on.
|
|
95
|
+
- **Local embedding model on the droplet.** A ~$14/mo shared CPU droplet has no headroom to host an embedding model without degrading the game server. Rejected; if embeddings are adopted they go through a serverless provider (the V4.R30 [#966](https://example.com/builders#/task/966) decision), consistent with the LLM-agnostic research ([#1069](https://example.com/builders#/task/1069)) "serverless, not local" finding.
|
|
96
|
+
- **Cross-encoder reranker.** A reranker over the fused top-k would lift precision but adds a model call per query (latency + spend) for a marginal gain at this corpus size. Out of scope; RRF over two lexical legs is the v1 ranker.
|
|
97
|
+
- **Filter scoping in application code (post-query).** Fetch top-k, then drop chunks the caller can't see in JS. Rejected outright: it's the exact read-side-bypass failure mode — a post-filter is one refactor away from leaking, and it can under-fill the result page. Scoping lives in the SQL WHERE clause so an unauthorized chunk is never selected in the first place.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ADR 0061 — Context-layer decomposition: a CLAUDE.md content charter + nested per-module docs
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-06-18
|
|
5
|
+
- **Track:** internal
|
|
6
|
+
- **Builds on:** [ADR 0016](<redacted>.md) (markdown only describes; the server/DB enforce — so trimming CLAUDE.md can never weaken authority), [ADR 0060](0060-gds-retrieval-layer.md) (the `/recall` retrieval half of "find what we know"; this ADR is the *instruction/navigation* half), [ADR 0024](<redacted>.md) (the Architect, which drives the deeper Path C module refactor)
|
|
7
|
+
- **Task:** 1222 (GDS-V4) — Path A of the 2026-06-18 "context cliff" research.
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
This project is almost entirely AI-authored. The owner's worry is concrete: as agents keep adding code and docs, the repo gets harder to scan and an agent's ability to change it safely degrades — a **context cliff**. The literature backs this up:
|
|
12
|
+
|
|
13
|
+
- **Length alone degrades reliability** — Chroma's *Context Rot* (2025, 18 models): accuracy falls as input grows, even on trivial tasks.
|
|
14
|
+
- **Position matters** — *Lost in the Middle* (TACL 2024): a fact buried mid-context is recalled far worse than at the edges.
|
|
15
|
+
- **Related-but-wrong content misleads** — a single near-miss distractor measurably lowers answers, and a large repo is a distractor factory.
|
|
16
|
+
- **Effective ≪ advertised** — NVIDIA's RULER (2024): usable context is roughly half the advertised window.
|
|
17
|
+
|
|
18
|
+
CLAUDE.md is the most-loaded artifact we have: Claude Code reads the root **in full, every session, regardless of length**. Anthropic's own guidance is **under ~200 lines**, with the rule *"a bloated CLAUDE.md causes Claude to ignore your actual instructions."* Ours was **351 lines** and mixed three kinds of content — durable framing, live state, and operational rules — so it over-spent the always-on budget *and* drifted (live-state prose goes stale between the DB and the file).
|
|
19
|
+
|
|
20
|
+
Cursor addresses the same problem by splitting one rules file into many small, **scoped, lazily-loaded** `.cursor/rules` files (loaded only when a matching file is in context). Claude Code has the native equivalent — **nested `CLAUDE.md` files load on demand** when an agent reads a file in that directory — and we were not using it: one root file, zero nested ones.
|
|
21
|
+
|
|
22
|
+
> On the owner's worry that "CLAUDE.md is deeply ingrained into making everything work": per [ADR 0016](<redacted>.md) it is **descriptive, not authoritative** — the server/DB enforce permissions and the ship pipeline, and editing the file grants no authority. So the risk is *not* that a wrong CLAUDE.md breaks enforcement. The risk is narrower and real: it over-spends the always-loaded context budget, and its live-state prose drifts. This ADR fixes that risk without touching enforcement.
|
|
23
|
+
|
|
24
|
+
## Decision
|
|
25
|
+
|
|
26
|
+
### 1. A content charter, stated at the top of CLAUDE.md
|
|
27
|
+
|
|
28
|
+
A short "Working with this file" section codifies what belongs in the root, with three rules:
|
|
29
|
+
|
|
30
|
+
- **The per-line test** (Anthropic's): *"Would removing this line cause an agent to make a mistake? If not, cut it."*
|
|
31
|
+
- **The decoder — what goes where:** durable framing + pointers + invariants → this file; live state (tasks/versions/ranks/costs) → the GDS DB; *why* a non-obvious choice was made → an ADR; multi-page how-to → a recipe; specifics of one module → that dir's nested `CLAUDE.md`; short per-rule learnings → the `learnings` table.
|
|
32
|
+
- **A size budget:** the root targets **≤ ~200 lines** (hard ceiling ~250). When it crosses, relocate detail *down the decoder* — don't append.
|
|
33
|
+
|
|
34
|
+
This is the "best practices for what goes in CLAUDE.md" the owner asked for, kept short so it doesn't itself bloat the file; the full rationale is this ADR.
|
|
35
|
+
|
|
36
|
+
### 2. Nested per-module `CLAUDE.md` (load on demand)
|
|
37
|
+
|
|
38
|
+
Thin `CLAUDE.md` files in the highest-traffic directories, loaded by Claude Code only when an agent works there. First set: `src/bongos/`, `src/bongos/routes/`, `scripts/gds/` (the GDS core + CLI, where the monoliths live — `db.js` ~219 KB, `ship.js` ~151 KB, `tasks.js` ~79 KB). Each follows a fixed template:
|
|
39
|
+
|
|
40
|
+
- **Purpose** — one paragraph: what this directory is for.
|
|
41
|
+
- **Key files / public surface** — the handful of entry points and what each does (the "function block" view — read this, dive into the implementation only as needed).
|
|
42
|
+
- **Gotchas / invariants** — the non-obvious rules that prevent mistakes here.
|
|
43
|
+
- **See also** — the relevant ADRs / recipes.
|
|
44
|
+
|
|
45
|
+
`CLAUDE.md` (not `README.md`) is deliberate: only `CLAUDE.md` is auto-loaded for the agent; READMEs serve humans. Where a dir already has a README (e.g. `art/`, `migrations/`), the nested `CLAUDE.md` stays a thin pointer + agent-only gotchas.
|
|
46
|
+
|
|
47
|
+
### 3. Hard invariants live in code, not prose
|
|
48
|
+
|
|
49
|
+
Anything that must never be skipped is a hook or CI check, not a CLAUDE.md sentence — prose is advisory, gates are deterministic. This is already largely true (`stop-refcheck.js`, `route-rank-check`, the grader pre-pass, `linkify-refs.js`). The charter makes the principle explicit so future rules land in the right layer.
|
|
50
|
+
|
|
51
|
+
### 4. Freshness is the seam to Path B
|
|
52
|
+
|
|
53
|
+
Hand-written nested docs will drift exactly like the owner fears. The **factual** half (the "public surface" list) is therefore a generation target for **Path B** — a tree-sitter symbol skeleton regenerated in CI the way `gen-diagrams.js` regenerates diagrams. Until then the *purpose / gotchas / see-also* halves are stable by design (matklad's ARCHITECTURE.md principle: record invariants, not line-level facts that rot).
|
|
54
|
+
|
|
55
|
+
## Consequences
|
|
56
|
+
|
|
57
|
+
- The root shrinks toward budget; per-module depth arrives only when relevant — less dilution, better instruction adherence (the documented failure mode).
|
|
58
|
+
- An agent (or human) gets a high-level map of each busy directory without reading every file — the "read generally, then dive in" property the owner asked for.
|
|
59
|
+
- **Risk:** nested docs drift until Path B generates their factual half — mitigated by keeping them thin + invariant-focused, plus a follow-up task for the generator.
|
|
60
|
+
- **Risk:** over-aggressive trimming could drop a load-bearing line — mitigated by *relocating* rather than deleting, and by the per-line test being conservative ("if unsure, keep").
|
|
61
|
+
- A future CI check can enforce the root size budget (filed as a follow-up).
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# ADR 0062 — Medusa: decoupling the GDS into an instance-model build platform
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-06-18
|
|
5
|
+
- **Track:** internal
|
|
6
|
+
- **Builds on:** [ADR 0010](0010-pms-architecture.md) (the GDS architecture being extracted), [ADR 0016](<redacted>.md) (the trust boundary — *core*, survives the split untouched), [ADR 0061](<redacted>.md) (the context-layer charter + nested docs — this ADR makes that the *portable methodology core*), [ADR 0004](0004-example-name-and-trademark-acceptance.md) (the civ-name "single config string" rule — generalized here into the branding contract), [ADR 0024](<redacted>.md) (the grader/architect/onboarding prompts that carry brand and must be parameterized)
|
|
7
|
+
- **Task:** [#1189](https://example.com/builders#/task/1189) (V4.R50) — the spike that **locks the boundary before the R51–R74 structural epics build**. Satisfies the design half of criteria **C8** (decouple the GDS into an instance model — "the GDS builds itself") and frames **C9** (AGPL open-source track).
|
|
8
|
+
- **Amended by:** [ADR 0108](<redacted>.md) — resolves §10's build-sequence assumption that instance content sits at the repo root into the configurable-root composition mechanism (a consumer installs the core as a dependency and injects an instance-root).
|
|
9
|
+
|
|
10
|
+
> **⚠ Renamed (2026-06-19):** the platform this ADR calls **Medusa** was renamed **Cloud Bongos** by owner directive. This record is kept verbatim as history; the rename decision (and what it touches vs. leaves as history) is [ADR 0064](<redacted>.md). Forward-facing config + docs now read "Cloud Bongos."
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
|
|
14
|
+
The goal (GDS-V4 Medusa cluster, planning tasks [#1188](https://example.com/builders#/task/1188)/[#1215](https://example.com/builders#/task/1215)): extract the GDS — the build/methodology platform — into **Medusa**, a configurable, self-hostable, AGPL, AI-first build platform. A host project supplies a **branding pack** and selects **feature-modules** (game, art-pipeline, discord, dev-box) that are *off by default*. **Customer 0** = Medusa on a plain vanilla brand, no modules — it builds itself (the C8 proof, [#1206](https://example.com/builders#/task/1206)). **Customer 1** = Example (OTB).
|
|
15
|
+
|
|
16
|
+
Today it is one fused codebase. A file-grounded coupling analysis (2026-06-18) found four seams:
|
|
17
|
+
|
|
18
|
+
1. **Process/HTTP** — one Express app + one Colyseus server. The GDS (`/api/gds`, hall, status) mounts host-agnostic *first*; the **game is the fall-through default**, not a module. Good news: `src/rooms/register.js` + `src/preview-server.js` already factor the game-room set out — the cleanest existing seam and the template for the cut.
|
|
19
|
+
2. **DB** — *one shared `pg` Pool*: `src/bongos/db.js` does `require('../db')` and reuses the game's pool. Game tables (`world_events`, `players`, migrations 001/002) and ~78 GDS tables share one pool and one flat migration sequence. `track` is a 2-value `CHECK (track IN ('product','internal'))` column on `versions`, woven into analytics + hall UI — an OTB-specific taxonomy.
|
|
20
|
+
3. **Brand/identity** — leaked, not single-config. "Example" is hardcoded across ~50 hits / 28 files; `example.com` across ~90 (cookie domain `.example.com`, OAuth origin, `*.dev.` SSH allowlist); "drachmae" in 23 files; "Off the Boats"/"Example" baked into **grader/architect/onboarding LLM prompts**. Only `devbox-app/src/brand.js` honors the single-config ideal.
|
|
21
|
+
4. **Methodology/skills** — already clean: `.claude/skills/builder-*` + idea/blocker/planning/status/recall are portable; the four `otb-*` art skills + `stop-sound.js` are host; `art/` is self-contained (no `src/` imports); Discord is already `DISCORD_BOT_ENABLED`-gated.
|
|
22
|
+
|
|
23
|
+
Why a spike-ADR first: 24 structural tasks (R51–R74) depend on this. They need **one** locked boundary, branding-contract schema, and module mechanism to build against, or they will diverge.
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
|
|
27
|
+
### 1. The boundary (the cut-line)
|
|
28
|
+
|
|
29
|
+
**The engine is host-agnostic and mounts modules; a host is a branding pack + a set of enabled modules.** Invert today's "game is the default fall-through" → the game is a module the OTB instance enables.
|
|
30
|
+
|
|
31
|
+
| Component | Portable core (Medusa) | Host instance (OTB) | Feature-module (gated) |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| `src/bongos/*` (routes, auth, grader, BFG, onboarding, recall) | ✅ core (prompts templatized) | — | — |
|
|
34
|
+
| `src/bongos/db.js` Pool | ✅ owns its own Pool | — | — |
|
|
35
|
+
| `src/db.js`, `players`/`world_events`, migrations 001/002 | — | ✅ | `game` |
|
|
36
|
+
| `src/rooms/`, `register.js`, `preview-server.js`, `public/` | — | ✅ | `game` (already factored) |
|
|
37
|
+
| `public-builders/` hall + `public-status/` | ✅ *engine* | copy/theme/lore | branding pack supplies strings |
|
|
38
|
+
| `art/` + `otb-*` skills + tile scripts | — | ✅ | `art-pipeline` (self-contained) |
|
|
39
|
+
| `discord-*.js`, `scripts/discord/`, `channels.json` | ✅ *engine* | channel layout + persona | `discord` (already flag-gated) |
|
|
40
|
+
| `box*.js`, `cf-tunnel.js`, `devbox-app/`, box skills | ✅ *engine* | `*.dev.` domains | `dev-box` |
|
|
41
|
+
| `.claude/skills/builder-*`, idea/blocker/planning, hooks | ✅ core | `stop-sound.js`, `otb-*` | — |
|
|
42
|
+
| `versions.track` + by_track analytics/UI | — | the 2-value taxonomy | configurable track set |
|
|
43
|
+
| domains/origins/cookie-domain | reads from config | hardcoded ×90 today | — |
|
|
44
|
+
|
|
45
|
+
### 2. The instance model
|
|
46
|
+
|
|
47
|
+
- **Engine entrypoint** (R51, [#1190](https://example.com/builders#/task/1190)): boots Express + `buildGdsRouter()` + hall + status, host-agnostic, **no Colyseus, no game/tile mounts** — sibling to `src/preview-server.js`. Today's `server.js` becomes the *OTB instance boot* (engine + game module).
|
|
48
|
+
- **A host instance** = branding pack (§3) + feature-module flags (§4) + owner/repo/OAuth binding (R62, [#1201](https://example.com/builders#/task/1201)) + its own DB (R52).
|
|
49
|
+
- Customer 0 = vanilla brand, no modules (the dogfood); customer 1 = OTB (game + art + discord + dev-box, drachmae, Greek pack).
|
|
50
|
+
|
|
51
|
+
### 3. The branding contract (`config/branding.*` — R53 keystone, [#1192](https://example.com/builders#/task/1192))
|
|
52
|
+
|
|
53
|
+
One per-instance config; **resolution precedence: env var → instance config → neutral starter** (fail to the plain default, never to OTB). A documented schema + neutral starter + an owner-LLM "fill-it-in" prompt. Fields the codebase proves are needed (each hardcoded today):
|
|
54
|
+
|
|
55
|
+
- **Identity:** `productName`, `worldName` (the in-world / civilization name), `company` (+ site).
|
|
56
|
+
- **Domains:** `publicOrigin`, `buildersOrigin`, `statusOrigin`, `cookieDomain`, box subdomain pattern (`*.dev.`/`sandbox-`/`term-`). *(Load-bearing security strings — a swap must not widen cookie/SSH scope.)*
|
|
57
|
+
- **Currency label** (R56, [#1195](https://example.com/builders#/task/1195)): "drachmae" → configurable; the cost-plus reward **math is unchanged**.
|
|
58
|
+
- **Tracks:** replace the 2-value `product|internal` CHECK with a configurable set (R52, [#1191](https://example.com/builders#/task/1191)).
|
|
59
|
+
- **Theme/palette + copy pack:** hall lore/primer/404 copy, Discord persona, and — critically — the **grader/architect/onboarding prompt brand header** must read the project name from config, or a non-OTB instance grades against the wrong project identity.
|
|
60
|
+
|
|
61
|
+
Customer 1 (OTB) pack = R57 ([#1196](https://example.com/builders#/task/1196)); vanilla = R74 ([#1216](https://example.com/builders#/task/1216)); the diagram generator reads it (R58, [#1197](https://example.com/builders#/task/1197)).
|
|
62
|
+
|
|
63
|
+
### 4. Feature-module selection (R54, [#1193](https://example.com/builders#/task/1193))
|
|
64
|
+
|
|
65
|
+
A **module registry**: each module declares what it contributes — route mounts, hall/status UI sections, skills, disciplines/achievements, DB migrations/tables, Colyseus rooms, pollers, hooks. An instance's config enables a set; resolution gates at mount time (`routes.js`/the entrypoint reads the enabled set). Modules: `game` + `art-pipeline` (R59, [#1198](https://example.com/builders#/task/1198)), `discord` + `dev-box` (R60, [#1199](https://example.com/builders#/task/1199)). The hall's only realtime feature (a Colyseus room) gets a non-Colyseus fallback over the pg-NOTIFY relay so a game-less instance keeps live updates (R55, [#1194](https://example.com/builders#/task/1194)).
|
|
66
|
+
|
|
67
|
+
### 5. DB carve-out (R52, [#1191](https://example.com/builders#/task/1191))
|
|
68
|
+
|
|
69
|
+
A GDS-owned Pool reading `DATABASE_URL`/`PG*` (not hard-defaulting to the `example` DB) — sever `src/bongos/db.js`'s `require('../db')`. A GDS-only migration sequence; game migrations 001/002 belong to the `game` module and are simply not applied on a non-game instance. Drop the hardcoded track taxonomy. **The trust boundary ([ADR 0016](<redacted>.md)) and per-request rank enforcement are core and unchanged.**
|
|
70
|
+
|
|
71
|
+
### 6. Config namespace (R61, [#1200](https://example.com/builders#/task/1200))
|
|
72
|
+
|
|
73
|
+
Consolidate `OTB_*`/`PMS_*` env prefixes + the `~/.config/otb/` path to a neutral product namespace driven by instance config, with back-compat shims (existing local sessions/boxes keep working). Owner bootstrap from instance config replaces migration 023's hardcoded `github_login='example-owner'` Archon literal (R62, [#1201](https://example.com/builders#/task/1201)).
|
|
74
|
+
|
|
75
|
+
### 7. Portable methodology core ↔ host context split (folds in [ADR 0061](<redacted>.md) + R66 [#1205](https://example.com/builders#/task/1205))
|
|
76
|
+
|
|
77
|
+
- **CLAUDE.md becomes the portable methodology core** Medusa ships: the working rules, session protocol, claim→ship lifecycle, the ADR 0061 **content charter**, and the **nested-`CLAUDE.md`** pattern. Instance-agnostic.
|
|
78
|
+
- **The host authors its own project-context file.** The OTB identity (company, vision, world, visual style — today's CLAUDE.md §§3/5/6/7) moves OUT of the shipped core into an instance-owned context doc + the branding pack. This is the concrete meaning of "move the project identity": **it is the core/host split, not tidying.**
|
|
79
|
+
- `medusa init` (R65, [#1204](https://example.com/builders#/task/1204)) scaffolds a new instance's context file from a neutral template + the owner-LLM fill prompt. The AI-first norm ("AI always writes the code") is documented in the core.
|
|
80
|
+
|
|
81
|
+
### 8. Session logs in the portable core (new task, folds in the redesign)
|
|
82
|
+
|
|
83
|
+
The §13 hand-maintained in-file index is an OTB-era artifact: it bloats the always-loaded file, drifts, and recurrently breaks (dangling-link bug recurs as idea 57, BFG F2, [#580](https://example.com/builders#/task/580)). Medusa's core ships a **generate-don't-narrate** model instead:
|
|
84
|
+
|
|
85
|
+
- The **DB session corpus is canonical** (already true — the BFG corpus + ADR 0051 full-transcript capture, [#1004](https://example.com/builders#/task/1004)).
|
|
86
|
+
- The context file carries at most an **auto-generated, bounded "recent sessions" snippet**, regenerated at deploy (the `gen-diagrams.js` pattern, branding-aware per R58); **full history renders in the hall + a generated index.** No instance hand-maintains a session-log index.
|
|
87
|
+
|
|
88
|
+
**Implemented — task [#1226](https://example.com/builders#/task/1226), 2026-06-19.** `scripts/gds/gen-session-index.js` generates `docs/session-log-index.md` (full history) + a bounded 10-entry snippet injected between markers into CLAUDE.md §13; it runs + commits at **ship** (`ship.js` `regenerateSessionIndex` — the realized form of "the gen-diagrams pattern") and is gated by `gen-session-index --check` in `fitness.js` (Check 5). §12 + the handoff-template drop the hand-maintained-index / manual-compaction chores; CLAUDE.md fell 342→300 lines and `ROOT_HARD_MAX` ratcheted 360→310. Two honest deltas from the wording above: **(a)** the index is generated from the canonical session **prose files** (`docs/session-logs/*.md`), not the DB — "DB corpus is canonical" presumes ADR 0051 transcript capture ([#1004](https://example.com/builders#/task/1004)), still Proposed/unshipped; generating from the files needs no unshipped dependency and makes a link **structurally undanglable** (retires idea 57 / [#580](https://example.com/builders#/task/580)). **(b)** A dedicated **hall** rendering of full narrative history is deferred until [#1004](https://example.com/builders#/task/1004) lands (the existing Archon session-ledger renders the `session_records` digests today); the generated index is the browseable full-history surface meanwhile.
|
|
89
|
+
|
|
90
|
+
### 9. A → B → C, folded into Medusa
|
|
91
|
+
|
|
92
|
+
The 2026-06-18 context-cliff research recommended three paths; each becomes a Medusa **core** capability:
|
|
93
|
+
|
|
94
|
+
- **A — context-layer decomposition** (ADR 0061, [#1222](https://example.com/builders#/task/1222)): the charter + nested per-module docs. This *is* the portable methodology core (§7). Done; carried into R66.
|
|
95
|
+
- **B — generated code/altitude map:** a tree-sitter symbol skeleton, regenerated in CI, feeding the *factual* half of the nested docs so they don't drift — keeping **every** instance's codebase navigable, complementing the recall layer ([ADR 0060](0060-gds-retrieval-layer.md)). (New core task.)
|
|
96
|
+
- **C — fitness functions in CI:** the **enforcement layer for this whole ADR.** A dependency-cruiser/ArchUnit-style gate that (i) prevents host/feature-module code from leaking into the portable core and vice versa — **mechanically enforcing the §1 cut-line and §4 module boundaries so the decoupling cannot silently rot back together** — and (ii) enforces the ADR 0061 CLAUDE.md budget + nested-doc presence, and (iii) asserts no module bypasses the §5 rank checks. Without C the boundary is a hope; with C it is a gate. (New core task — the single highest-leverage guarantee that Medusa *stays* decoupled.)
|
|
97
|
+
|
|
98
|
+
### 10. Build sequence
|
|
99
|
+
|
|
100
|
+
R50 (this ADR) → **R51 entrypoint + R52 DB pool** (independent foundations) → **R53 branding contract (keystone — most epics cross-ref it)** → **R54 module system** → parallelizable de-brand epics (R55–R62, R66 CLAUDE.md split, the new session-log + Path-B tasks) → **Path-C fitness functions** (wired as a dep of "decoupling done") → R63 docker / R64 upgrade / R65 init → **R67 C8 proof** (a second, non-game branded instance ships a task). C9 (R68–R73: AGPL ADR/LICENSE, governance, public mirror, delayed export, lineage proof) proceeds on its own track.
|
|
101
|
+
|
|
102
|
+
## Consequences
|
|
103
|
+
|
|
104
|
+
- A clear, file-grounded target the 24 epics build against — one boundary, one contract, one module mechanism.
|
|
105
|
+
- The trust boundary + rank enforcement stay core and unchanged; a feature-module must never be able to bypass server-side rank checks (a Path-C assertion).
|
|
106
|
+
- **Hardest knots (called out so the epics plan for them):** (1) shared Pool + flat migrations; (2) the brand-saturated, *not* data-driven hall UI (~50 brand hits, drachmae in 23 files) — the largest mechanical job; (3) inverting game-as-default into a mounted module; (4) domains woven into auth/security — config swaps must not widen cookie/SSH scope; (5) brand inside LLM prompts (grader/architect/onboarding).
|
|
107
|
+
- **Risk: scope.** Mitigation: R53 keystone first; modules incremental; Path-C enforces no regression; customer-0 dogfood ([#1206](https://example.com/builders#/task/1206)) is the falsifiable proof the cut actually worked.
|