@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,72 @@
|
|
|
1
|
+
# ADR 0046 — Sandbox-first review in the dev-box ship flow
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-11
|
|
4
|
+
**Context:** GDS-V3, task [#927](https://example.com/builders#/task/927). Builds directly on [ADR 0044](<redacted>.md) (the per-box live game preview / "sandbox") and the three-state ship lifecycle in `scripts/gds/ship.js` (Phase 5). Companion to [ADR 0031](<redacted>.md) (dev boxes) and [ADR 0016](<redacted>.md) (the gate is a workflow aid, not an authority surface).
|
|
5
|
+
**Status:** Accepted.
|
|
6
|
+
**Track:** `internal` (dev-system / builder experience).
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
ADR 0044 gave every named-tunnel dev box a live sandbox (`sandbox-<login>.example.com`) serving the box's `/workspace` working tree — but nothing in the development flow *uses* it. A builder on a box can still run `/builder-ship` on a game change they have never seen running, and the pipeline carries it straight toward prod. The sandbox exists; the methodology doesn't ask for it. Lars's directive (2026-06-11): anytime a builder is on a dev box, they should **first stage their work on their sandbox to see it, have the opportunity to suggest changes, and only then decide to complete it and ship to the main game**.
|
|
11
|
+
|
|
12
|
+
## Decision
|
|
13
|
+
|
|
14
|
+
**Make staging-and-reviewing on the sandbox a first-class, enforced step of the dev-box ship flow.** Two pieces:
|
|
15
|
+
|
|
16
|
+
### 1. `/builder-stage` — staging as a named verb (`scripts/gds/sandbox-stage.js`)
|
|
17
|
+
|
|
18
|
+
Staging = restart `box-game-preview.service` so the sandbox serves the current `/workspace` working tree, health-check `127.0.0.1:3000`, and write a **staged marker** (`otb-sandbox-staged.json` in the OS temp dir) recording a SHA-256 stamp of exactly what was staged. The stamp is a **content hash scoped to the previewable game surfaces** (`git ls-files` + `git hash-object` over `public/`, `src/world/`, `src/rooms/`, `server.js`, tracked + untracked): editing a game file — committed or not — changes it, while editing docs/GDS files or merely `git add`/committing the *same* content does not. So "staged" can never silently mean "staged some older version of the game", and the natural stage → review → commit → ship flow keeps the marker fresh. The command prints the sandbox URL and tells the builder to review before shipping. Sub-commands: `status` (is the sandbox current?), `url`.
|
|
19
|
+
|
|
20
|
+
### 2. The ship gate — `ship.js` checks the marker before the claim resolves
|
|
21
|
+
|
|
22
|
+
`shipMain()` runs the gate after local verification (scanner + smoke + empty-diff guard) and **before** `POST /claims/:id/resolve` — i.e. before anything counts, so an abort leaves the claim active and the builder iterates freely. Decision table (pure `decideShipGate()`, unit-tested):
|
|
23
|
+
|
|
24
|
+
| Condition | Action |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `--skip-stage` | skip (escape hatch; not routine) |
|
|
27
|
+
| not a dev box (no `/etc/otb/box.env`) | skip — laptop/operator sessions unchanged |
|
|
28
|
+
| diff touches no previewable game surface | skip — the sandbox shows the game; GDS/doc/infra diffs have nothing to look at there |
|
|
29
|
+
| box on the quick tunnel (no `BOX_PREVIEW_HOSTNAME`) | skip with notice — ADR 0044 already scopes the sandbox to named-tunnel boxes |
|
|
30
|
+
| staged marker matches the current tree stamp | pass — already staged + reviewable; proceed |
|
|
31
|
+
| stale/absent marker, **interactive TTY** | stage now, print the URL, ask: "Reviewed on the sandbox — ship this to the main game? [y/N]" — No aborts with the claim intact |
|
|
32
|
+
| stale/absent marker, **non-interactive** | stage so the sandbox shows the work, then **abort with the claim intact and the review URL** — never hangs on stdin, never silently skips the review. This is the dominant path: a builder's Claude session runs `ship.js` through a non-TTY shell, so the agent relays the URL, the human reviews (and suggests changes), and the re-run passes on the now-fresh marker |
|
|
33
|
+
|
|
34
|
+
"Previewable game surfaces" = `public/`, `src/world/`, `src/rooms/`, `server.js` — the paths the sandbox host actually renders (host-routing sends `sandbox-*` to the game). The check covers both the committed diff **and** uncommitted working-tree edits, since the sandbox serves the working tree.
|
|
35
|
+
|
|
36
|
+
### Failure posture: the gate is a review aid, not a wall
|
|
37
|
+
|
|
38
|
+
If staging itself fails (service won't start, health-check times out), the ship **warns loudly and continues**. The hard quality gates remain the scanner, smoke, and the grader panel; a broken preview service must not be able to brick shipping. Likewise the gate runs client-side in `ship.js` by design — it shapes the cooperative builder's workflow; it is not a trust boundary (ADR 0016: server-side rank checks and the grader are what enforce).
|
|
39
|
+
|
|
40
|
+
## Alternatives considered
|
|
41
|
+
|
|
42
|
+
- **Server-side enforcement (refuse `resolve` without a staged attestation).** Rejected: the server cannot verify what a builder's box showed them — the attestation would be client-claimed anyway, adding schema + API surface for no real guarantee. Workflow nudges belong client-side; trust stays where ADR 0016 put it.
|
|
43
|
+
- **Gate inside `/builder-ship`'s skill prose only (no code).** Rejected: skills guide the agent but nothing re-checks; the point is that an un-staged game change *reliably* pauses for review.
|
|
44
|
+
- **Always require review for every diff.** Rejected: the sandbox renders the game; staging a `docs/` or GDS change shows nothing and just adds a restart + prompt of pure friction.
|
|
45
|
+
|
|
46
|
+
## Consequences
|
|
47
|
+
|
|
48
|
+
- The dev-box loop becomes: edit → `/builder-stage` → review at `sandbox-<login>` → iterate → `/builder-ship` (gate passes on the fresh marker) → main game. A builder who skips straight to ship gets staged + paused once, in-line.
|
|
49
|
+
- The non-interactive abort is deliberately bypassable by an immediate re-run (the first run staged, so the marker is fresh). That is acceptable: the gate is cooperative workflow shaping, and the agent in between is instructed to put the URL in front of the human. A truly autonomous box run that must not pause passes `--skip-stage` explicitly.
|
|
50
|
+
- Laptop sessions (no box.env) are completely unaffected — including the operator's own ships and `/merge-mode`.
|
|
51
|
+
- `--skip-stage` exists for genuine breakage; like `--skip-grade` it is an escape hatch, not a routine flag.
|
|
52
|
+
- The marker lives in the box's temp dir — it is per-box, survives within a boot, and is naturally invalidated by edits (stamp mismatch). No DB schema, no new API surface, $0.
|
|
53
|
+
|
|
54
|
+
## Amendment — 2026-06-13, task [#1048](https://example.com/builders#/task/1048): record the approval + position the link in chat
|
|
55
|
+
|
|
56
|
+
Lars's directive (2026-06-13), after the gate shipped: (1) the sandbox link must be **positioned prominently in chat** when a change is ready to review, and (2) the builder's review-and-approve must be **recorded**, not left as an ephemeral marker. Both are now built; this amendment supersedes two specific stances above.
|
|
57
|
+
|
|
58
|
+
- **Prominent in-chat link.** `runShipGate` now prints a delimited `🔍 REVIEW REQUIRED` block with the sandbox URL alone on its own line (both the dominant non-TTY `stage_abort` path and the TTY prompt), and the `/builder-ship` skill instructs the agent to surface that URL as a standalone, clickable callout in chat — never buried in command output. The "agent relays the URL" hand-off in the Decision table is unchanged in mechanism; only its presentation is now first-class.
|
|
59
|
+
|
|
60
|
+
- **Recorded self-approval (supersedes "no DB schema, no new API surface").** The approval is now a durable fact. When the builder approves a staged game change — a TTY `y`, or an explicit `--approved` re-run on the non-TTY path — `ship.js` threads `{ stamp, url, method }` through the existing `POST /claims/:id/resolve` call (no new endpoint); the server stamps `by` + `at` authoritatively and persists the blob to a new `tasks.sandbox_review jsonb` column (migration 093). The original ADR argued *against* persisting the attestation because the server "cannot verify what a builder's box showed them." That reasoning still holds for *verification* — this record is an explicit, server-attributed **sign-off** ("builder X approved this exact game-surface stamp at time T"), which is a real and useful audit fact, not a claim that review happened. The scope is unchanged: **dev-box + named-tunnel + game-surface only, self-approval (no second approver), failure-open.**
|
|
61
|
+
|
|
62
|
+
- **The `--approved` flag records a sign-off; it never bypasses review.** It is honored only when the current game state is already staged-fresh (`decideShipGate → 'pass'`) — i.e. a re-run after a prior stage. A stale/absent marker still stages and pauses, so there is nothing to "pre-approve." A bare fresh-marker re-run *without* `--approved` still ships (back-compat / failure-open) but records nothing and prints a nudge.
|
|
63
|
+
|
|
64
|
+
- **Cost:** one nullable `jsonb` column + a field on an existing request body. Still no new endpoint; ~$0.
|
|
65
|
+
|
|
66
|
+
## Amendment — 2026-06-14, task [#1056](https://example.com/builders#/task/1056): the gate (and [#1048](https://example.com/builders#/task/1048)'s link + recorded approval) now also cover LOCAL clones
|
|
67
|
+
|
|
68
|
+
ADR 0044/0052 gave dev boxes a sandbox; the gate above and [#1048](https://example.com/builders#/task/1048)'s prominent link + recorded self-approval were written for that box path. Builders working on a full **local** clone (laptop) now get the identical loop — served privately at `http://localhost:<port>` (default 3100, `$OTB_PREVIEW_PORT` to override), **no Cloudflare tunnel, no public URL, $0** — via `scripts/gds/local-preview.js` (`npm run preview`) running the same ADR 0052 game-only entry (`src/preview-server.js`). Lars's directive (2026-06-14): "make the sandbox available for those also building locally" + "follow 1048 practice for the local sandbox as well."
|
|
69
|
+
|
|
70
|
+
- **One resolver, two contexts.** `resolvePreviewContext()` in `sandbox-stage.js` returns a uniform `{ kind, url, healthUrl, restart }` — **box** (keyed by `/etc/otb/box.env`) or **local** (keyed by the presence of `src/preview-server.js`). The whole gate flows through it unchanged: `decideShipGate`'s `onBox` became `gateActive` (box OR local), `stage()` (re)starts the resolved preview, and [#1048](https://example.com/builders#/task/1048)'s `🔍 REVIEW REQUIRED` block + the `{ stamp, url, method }` recorded approval carry the **local** URL; the marker carries `kind:'local'`.
|
|
71
|
+
- **Enforcement parity (supersedes "Laptop sessions … are completely unaffected").** A game-surface ship from a full local clone now stages + pauses for review exactly as on a box, and a TTY `y` / `--approved` re-run records the same durable self-approval. The `--skip-stage` escape hatch is unchanged. A **partial** clone with no game (`src/preview-server.js` absent) still has no sandbox and is unaffected, and **`/merge-mode` is still unaffected** (it lands `confirmed` tasks outside `shipMain`). This does mean the operator's own laptop game-surface ships are now gated too — consistent with "enforced like the box"; `--skip-stage` covers the exceptions.
|
|
72
|
+
- **Cost:** $0 — localhost, no tunnel, no new server surface (reuses [#1048](https://example.com/builders#/task/1048)'s `tasks.sandbox_review` + resolve field).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# ADR 0047 — Discord `#bugs` intake → GDS tasks, straight-through, with a layered anti-abuse guard
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-06-12
|
|
5
|
+
- **Task:** [#931](https://example.com/builders#/task/931)
|
|
6
|
+
- **Extends:** [ADR 0033](<redacted>.md) (Discord bot service principal, the `#ideas` → `idea_inbox` inbound path), [ADR 0016](<redacted>.md) (trust boundary), [ADR 0034](<redacted>.md) (the rank ladder this leans on)
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
Builders wanted somewhere to drop a small fix they've spotted without context-switching into a full claim. The `#ideas` channel already exists (ADR 0033 §F6), but ideas land in `idea_inbox` and wait for human triage (`/idea-triage`) before becoming work. Lars's directive (2026-06-12): **bugs skip triage and become GDS tasks (`kind='bug'`) directly** — claimable work, straight through.
|
|
11
|
+
|
|
12
|
+
That directive carries an embedded red-team question the task made a hard requirement: *if bug-filing creates tasks with no triage gate, how do we stop someone injecting nonsense / spam / malicious bugs into the queue?* The straight-through path and the abuse guard are the **same** task — neither ships without the other.
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
A `#bugs` Discord channel (added to `docs/discord/channels.json`) whose messages the bot turns into GDS tasks (`kind='bug'`) via a new in-process module `src/bongos/discord-bugs.js`, structurally parallel to `discord-inbound.js` but routing to `db.createTask` instead of `inbox.captureIdea`. The intake is guarded **in depth**, all server-side, all fail-closed:
|
|
17
|
+
|
|
18
|
+
1. **Linked-builder-only.** An *unlinked* Discord author cannot create a task — their message is ignored (logged, not filed). Unlike `#ideas` (which captures unattributed ideas), making *claimable work* requires being an accountable, known builder who has completed the GitHub→Discord OAuth link. This is the single biggest anti-abuse lever: no anonymous injection.
|
|
19
|
+
2. **Rank-aware landing.** A linked **Metic+** builder's bug lands `status='ready'` — instantly claimable, the true straight-through path their trust earns. A linked **Xenos/Thetes** builder's bug lands `status='backlog'`, where a Metic+ promotes it (`POST /tasks/:id/promote`, the one-click confirm-to-ready already used by `/idea-triage`). The landing is decided server-side from the reporter's **live** rank, never from anything in the message. This mirrors the trust ladder used everywhere else in the GDS.
|
|
20
|
+
3. **Per-builder rate-limit.** At most N bug-tasks per builder per rolling hour (default 5, `DISCORD_BUGS_RATE_PER_HOUR`), counted from `audit_log` (`db.countRecentDiscordBugFilings`). Over-limit filings are ignored **and audited** (`response_status=429`, `surface='discord_bot'`) so a spammer surfaces in forensics. The count fails *open* (the linked-builder + audit trail still hold a spammer accountable) so a transient DB hiccup never loses a real report.
|
|
21
|
+
4. **Body screen.** Empty / too-short (< 8 chars) / text-free (no letters) messages are rejected before any DB write; title + body are capped to the GDS limits.
|
|
22
|
+
|
|
23
|
+
Every filing is audited with `surface='discord_bot'` (`route='discord:bugs'`), so the existing `GET /audit?surface=discord_bot` forensic surface covers this path with no new endpoint.
|
|
24
|
+
|
|
25
|
+
### Why rank-aware landing rather than always-ready or always-backlog
|
|
26
|
+
|
|
27
|
+
- *Always-ready* honors "straight-through" most literally but puts a newcomer's (or a freshly-compromised account's) unreviewed text straight into the claimable queue.
|
|
28
|
+
- *Always-backlog + confirm* is safest but isn't really "straight-through" — it's just lighter triage, which defeats the point for the trusted builders this feature is for.
|
|
29
|
+
- *Rank-aware* gives trusted builders (Metic+) the genuine straight-through path while gating exactly the population the red-team question worries about (newcomers / low-trust), at zero extra ceremony for the people who earned trust. It reuses machinery that already exists (rank, `promote`), so it adds a decision function, not a subsystem.
|
|
30
|
+
|
|
31
|
+
## Consequences
|
|
32
|
+
|
|
33
|
+
- **Config / go-live:** inert until `DISCORD_BUGS_CHANNEL_ID` (env, or `bugs_channel_id` in `~/.config/otb/discord-bot.json`) is set **and** the bot is enabled (`DISCORD_BOT_ENABLED=true`) with the Message Content intent — exactly like `#ideas`. The channel itself is created/edited by the ADR 0037 channel reconciler from `channels.json`.
|
|
34
|
+
- **Version:** filed bugs default to `DISCORD_BUGS_VERSION_ID` (default `GDS-V3`, the open internal version, since most builder-filed bugs are about the dev system). An Archon can re-home a game bug to a product version. Reward defaults to `DISCORD_BUGS_CREDITS` (10).
|
|
35
|
+
- **Trust boundary intact (ADR 0016):** the bot asserts only *who* posted; the landing state is derived from the live GDS rank, never trusted from the message. The bot grants nothing.
|
|
36
|
+
- **Deferred:** a dedicated abuse-flag widget on the `/watch` red-team view. The audit trail (`surface='discord_bot'`, the 429 rejections) already makes abuse queryable; a bespoke view is a follow-up if volume warrants it.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# ADR 0048 — "Works in Progress" information-architecture split: work board owns it, status mirrors a thin slice
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-06-12
|
|
5
|
+
- **Task:** [#938](https://example.com/builders#/task/938)
|
|
6
|
+
- **Supersedes presentation choices in:** the versions render on `public-status/` (PMS-V2 [#41](https://example.com/builders#/task/41) done-when checklist; Phase 5 / [#94](https://example.com/builders#/task/94) three-segment bar)
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
Two surfaces independently rendered a "Works in Progress" section, both reading the same public `/api/gds/public/progress` feed:
|
|
11
|
+
|
|
12
|
+
- **status.example.com** (`public-status/`) — a public, no-auth, off-box mirror (ADR 0029). It showed every version with name, track tag, a 3-segment lifecycle bar, a "% complete" line, and an expandable done-when criteria checklist.
|
|
13
|
+
- **The builders' work board** (`public-builders/work.html`, `builders.example.com/work`) — sign-in-gated. Its "Works in Progress" section (`#wip-scroll`) was authored to be the source of truth (live in-flight claims + the same bars) **but the section carried a `hidden` attribute that no code ever removed**, so it never displayed — the page jumped straight to the claimable queue.
|
|
14
|
+
|
|
15
|
+
Lars asked that the work board become the canonical home of Works in Progress, and that the public status page keep only a thin mirror: the **active** endeavours, **title + short description only** — "as we are saying it should stay there."
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
1. **The work board is the canonical home.** Unhide `#wip-scroll`. It shows, for **every** version: title, short description, the 3-segment progress bar, and the done-when criteria checklist — plus the live "borne by hand right now" in-flight table. (Fixing the never-unhidden bug is part of this.)
|
|
20
|
+
|
|
21
|
+
2. **The public status page is a thin mirror.** It shows only **active** endeavours, each as **title + short description**. No bars, no counts, no criteria. The now-dead version-bar and done-when CSS/JS on `public-status/` is removed.
|
|
22
|
+
|
|
23
|
+
3. **"Active" = underway, not past, and actually started.** A version qualifies when `status NOT IN ('shipped','frozen')` **and** it has at least one `done_when_criteria` seeded (`criteria_count > 0`). The criteria-count test is what excludes a *future placeholder* — e.g. the next-version stub created at version close (GDS-V4 today), which has no criteria yet — without needing a new schema flag. When that version is scoped in a planning session and gains criteria, it appears on the public board automatically.
|
|
24
|
+
|
|
25
|
+
4. **"Short description" reuses `versions.done_when`.** No new column. `done_when` is the senate-voice narrative of what a version *is*; the criteria checklist (a separate `done_when_criteria` table) is "their criteria." The two map cleanly onto "short description" and "criteria," so no data model change was needed. `db.versionProgress()` now also returns `done_when` and `criteria_count` (a `db.js`-only join — no migration).
|
|
26
|
+
|
|
27
|
+
## Consequences
|
|
28
|
+
|
|
29
|
+
- The public page no longer leaks per-version progress detail; it reads as a short, dignified list of what the city is building. Builders who want the full picture (bars, criteria, in-flight) go to the work board.
|
|
30
|
+
- The two surfaces still cannot share rendering code: `public-status/` is copied byte-identical to the off-box GitHub Pages mirror (ADR 0029) and cannot import from `public-builders/`. They remain parallel implementations of a shared **data** contract (`/api/gds/public/progress` + `/versions/:id/done-when`).
|
|
31
|
+
- A version with an empty `done_when` shows as a bare title on both surfaces. V2 and V3 had empty `done_when`; short descriptions were written for them as part of [#938](https://example.com/builders#/task/938).
|
|
32
|
+
- The "future placeholder" rule is conventional, not enforced: a version is hidden from the public board precisely while it has zero criteria. This is the desired lifecycle (scope it → it shows), but it does mean seeding a single criterion makes a version public.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# ADR 0049 — The split parallel-safety contract (detect-at-merge for humans, gate for the autonomous runner)
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-13
|
|
4
|
+
**Context:** GDS-V3, task [#881](https://example.com/builders#/task/881), step [6/8] of the `touches[]` simplification cluster ([#875](https://example.com/builders#/task/875)–[#883](https://example.com/builders#/task/883)). Records the parallel-safety model that the cluster's code changes established — see the architect audit [`docs/audits/<redacted>.md`](../audits/<redacted>.md). Supersedes the *predictive* framing implicit in [ADR 0010](0010-pms-architecture.md) (GDS architecture) and [ADR 0015](<redacted>.md); complements [ADR 0017](<redacted>.md) (the "a hard block turned correct work into deferred backlog" lesson).
|
|
5
|
+
**Status:** Accepted.
|
|
6
|
+
**Track:** `internal` (development system / methodology).
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
For most of GDS's life, parallel-safety rested on a **prediction**: every task declared a `touches[]` file list up front, and a stack of machinery tried to prove — *before any code existed* — that two claims wouldn't collide. The audit found this stack was three disagreeing matchers across a claim-time 409, a ship-time drift scanner, and a CI gate, mis-documented as a "warning" when it actually withheld credits. It was the felt source of "slowed builders": code legitimately wanders into files nobody predicted, and a wrong prediction blocked or de-credited correct, mergeable work.
|
|
11
|
+
|
|
12
|
+
The audit's adversarial review (multi-builder, autonomous-runner, merge-reality lenses) reached a split verdict, **not** a blanket "delete prediction, detect at merge." A blanket pivot was REFUTED by two lenses: the autonomous `--parallel` runner pushes to `main` with **no human to resolve a conflict**, and `git` is blind to *semantic* clobbers between two edits that don't textually overlap. So the honest contract is conditional on **who** is building.
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
Parallel-safety is a **split contract**, keyed on the build path:
|
|
17
|
+
|
|
18
|
+
### (a) Interactive, single-human path — detect at merge
|
|
19
|
+
|
|
20
|
+
The ground-truth collision detector is **`git merge`** at land time (`/builder-ship`'s auto-merge and `/merge-mode`). A real overlap surfaces as a merge conflict, resolved by a human at the authoritative moment — *after* the code exists.
|
|
21
|
+
|
|
22
|
+
- **Single-task atomicity** is guaranteed by the `uniq_active_claim_per_task` partial unique index (`migrations/003_pms.sql:130-131`): two sessions cannot both hold an active claim on the same task. This is the one hard invariant that always holds.
|
|
23
|
+
- The **claim-time `touches[]` overlap signal is advisory** (task [#880](https://example.com/builders#/task/880)): on overlap, `claimTask` returns an `overlap_warning` and the claim **succeeds**. `touches[]` is an advisory hint at the expected blast radius — not an editing fence (the ADR 0017 lesson).
|
|
24
|
+
- The predictive **drift scanner + CI gate + expand-on-drift flow were deleted** (task [#879](https://example.com/builders#/task/879), ~737 LOC). The subagent grader reads the **real committed diff** (touches-independent since [#536](https://example.com/builders#/task/536)) and flags genuinely out-of-scope writes there.
|
|
25
|
+
|
|
26
|
+
### (b) Autonomous `--parallel N` path — keep a mechanical disjointness gate
|
|
27
|
+
|
|
28
|
+
The overnight runner fans concurrent, unsupervised sub-agents and direct-pushes to `main`. Here a **file-footprint disjointness precondition is retained as a HARD gate**, alongside `parallel_safe = true`:
|
|
29
|
+
|
|
30
|
+
- `claim.js --enforce-touches` (which the `overnight-builder` routine always passes) sets `enforce_touches`, so `claimTask` / `claimTasksBatch` **refuse** with `TOUCHES_CONFLICT` on prefix-aware overlap rather than merely warning.
|
|
31
|
+
- **`parallel_safe` and footprint-disjointness are orthogonal and both required.** `parallel_safe` covers *non-file* hazards the runner can't see in a diff — a server restart, a shared mutable DB row, a long deploy. Footprint-disjointness covers file adjacency. Neither subsumes the other; the runner needs both true.
|
|
32
|
+
|
|
33
|
+
### Shared mechanics
|
|
34
|
+
|
|
35
|
+
- One **prefix-aware matcher** (`src/bongos/path-match.touchesOverlap`) is the single definition of "adjacent" across `claimTask`, `claimTasksBatch`, and the session optimizer — so the planner, the advisory warning, and the parallel gate all agree.
|
|
36
|
+
- The `claimable_tasks` **view stays on the permissive Postgres `&&` (exact-element) operator** by design: making the *display* filter prefix-strict would over-serialize the visible queue given V3's coarse top-level-dir `touches[]`. The view is a soft hint; the authoritative prefix-aware check lives in code.
|
|
37
|
+
|
|
38
|
+
## Consequences
|
|
39
|
+
|
|
40
|
+
- **Interactive builders are never blocked by a prediction.** They may be warned; they decide. Wrong predictions cost nothing.
|
|
41
|
+
- **The autonomous runner stays safe** — it still refuses to fan two same-footprint tasks into concurrent agents.
|
|
42
|
+
- The asymmetry is deliberate and must be preserved: do not "simplify" the autonomous path down to advisory, and do not promote the interactive path back to a hard block.
|
|
43
|
+
|
|
44
|
+
## The future-review trigger (deferred, conditional)
|
|
45
|
+
|
|
46
|
+
The full removal of the footprint gate — replacing it with **merge-time auto-park detection** (a `confirmed→shipped` `git merge` conflict auto-parks the task with a "rebase against main" note) **plus a runner-side post-fan-out footprint diff check** — was deliberately **deferred**. Revisit it **only when 2+ distinct human builders regularly edit concurrently** (today there is one human builder running many sessions under one identity — see the `feedback_parallel_sessions_not_anomaly` memory). At that point: either stand up the merge-time-detection + runner-footprint-check pair and drop the advisory column entirely, or keep the matcher-fixed advisory. Until then, the advisory signal + `parallel_safe` + the `uniq_active_claim_per_task` index are sufficient. Zero occurrences of the protected concurrent-human-edit scenario are documented at current scale.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# ADR 0050 — Device-Flow admission posture: invite-gated by default
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-13
|
|
4
|
+
**Context:** GDS, security report SR-19 / task [#1000](https://example.com/builders#/task/1000). Closes an admission inconsistency between the two GitHub enrollment paths in `src/bongos/auth.js`. Extends the web-sign-in approval gate added 2026-05-30 (the `access_requests` `'invited'` model) and is governed by the trust-boundary rule in [ADR 0016](<redacted>.md) (the server enforces admission, not the client).
|
|
5
|
+
**Status:** Accepted.
|
|
6
|
+
**Track:** `internal` (development system / builder onboarding + access control).
|
|
7
|
+
|
|
8
|
+
## Problem (SR-19)
|
|
9
|
+
|
|
10
|
+
The two ways a GitHub account becomes a builder were gated **inconsistently**:
|
|
11
|
+
|
|
12
|
+
- **`completeWebFlow`** (the web hall sign-in, `/api/gds/auth/web/callback`) refused a **first-time** GitHub account unless an Archon had marked their `access_requests` row `'invited'`. Established builders (a `builders` row already exists for their `github_id`) always passed.
|
|
13
|
+
- **`pollDeviceFlow`** (the CLI `/builder-setup` device flow, `/api/gds/auth/device/poll`) called `upsertBuilder()` **unconditionally** the moment GitHub returned an access token. So **any GitHub account could self-enroll as a Xenos and mint a never-expiring CLI session with no Archon approval whatsoever** — simply by running `/builder-setup` and authorizing on github.com.
|
|
14
|
+
|
|
15
|
+
This directly contradicted the documented posture that **Archon admission is the only gate** for new builders (CLAUDE.md / [ADR 0016](<redacted>.md) / [ADR 0035](<redacted>.md)). The web door was locked; the CLI door was wide open. The blast radius is bounded by the Xenos sandbox (a self-enrolled account lands at the lowest rank, can only claim `xenos_claimable` tasks, and is kept out of the permission/methodology core), but it is still an unauthorized foothold inside the trust boundary and an inconsistency an auditor will (and did) flag.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
**Make both enrollment paths consistent: invite-gated by default, with an env escape-hatch.**
|
|
20
|
+
|
|
21
|
+
1. `pollDeviceFlow` now applies the **same gate** `completeWebFlow` applies, before `upsertBuilder`:
|
|
22
|
+
- Look up `getBuilderByGithubId(ghUser.id)`.
|
|
23
|
+
- If **no** existing builder row AND open enrollment is off AND the login has **no** `'invited'` access request → throw the same `NOT_APPROVED` error the web flow throws.
|
|
24
|
+
- **An existing builder ALWAYS passes.** The gate only blocks genuine first-time entries, so an already-approved builder can never be locked out of CLI re-enrollment (e.g. re-auth on a new box). This early-pass is the load-bearing invariant.
|
|
25
|
+
|
|
26
|
+
2. Both paths share one **pure decision helper**, `firstTimeAdmissionBlocked({ existing, approved })`, so the web and CLI gates **cannot drift** out of sync the way they just did.
|
|
27
|
+
|
|
28
|
+
3. **Escape-hatch:** `openEnrollmentEnabled()` reads the environment flag **`OTB_OPEN_XENOS_ENROLLMENT`** (truthy = `'1'` or `'true'`). When set, the invite gate is skipped in **both** paths and any GitHub account self-enrolls as a Xenos again — restoring the old fully-open behavior **without a code change or redeploy**, for an owner who deliberately wants open enrollment. **Default (unset) = invite required.**
|
|
29
|
+
|
|
30
|
+
4. The CLI surfaces the refusal gracefully: the `device/poll` route returns a structured `403 { state: 'not_approved' }` (mirroring the web flow's `NOT_APPROVED` branch), and `scripts/gds/setup.js` prints a friendly "this account hasn't been approved yet — ask an Archon to invite you, then re-run `/builder-setup`" message instead of a raw error dump.
|
|
31
|
+
|
|
32
|
+
## Alternatives considered
|
|
33
|
+
|
|
34
|
+
- **Formally accept open Xenos enrollment** (document the device flow as intentionally open, relying on the Xenos sandbox to contain the risk). Rejected as the *default*: it contradicts the stated "Archon admission is the only gate" posture and leaves an un-audited self-enrollment door, even if low-privilege. It remains available **on demand** via `OTB_OPEN_XENOS_ENROLLMENT=1` — so this ADR doesn't foreclose it, it just makes "secure" the default and "open" the explicit, reversible owner choice.
|
|
35
|
+
- **A repo-collaborator / GitHub-org check** as the gate. Rejected: newcomers build on hosted dev boxes with no repo access, so there is deliberately no collaborator check anywhere (2026-06-07). The `access_requests` `'invited'` model is the established admission signal; reusing it keeps one gate, not two.
|
|
36
|
+
- **Gate inside `upsertBuilder`** (a DB-layer guard). Rejected: `upsertBuilder` is also the update path for existing builders and is called from non-admission contexts; pushing an admission policy into it would conflate "create a record" with "admit a person." The gate belongs at the two flow entry points, sharing one pure helper.
|
|
37
|
+
|
|
38
|
+
## Consequences
|
|
39
|
+
|
|
40
|
+
- **Secure by default:** a fresh GitHub account can no longer self-enroll via `/builder-setup` — it needs an Archon invite, exactly like the web hall.
|
|
41
|
+
- **No existing builder is locked out:** the existing-row early-pass guarantees re-auth/re-enroll keeps working for anyone already admitted.
|
|
42
|
+
- **Reversible without a deploy:** an owner who wants open enrollment flips `OTB_OPEN_XENOS_ENROLLMENT=1` in the systemd `EnvironmentFile` and restarts the process.
|
|
43
|
+
- **No drift:** the shared `firstTimeAdmissionBlocked()` helper means a future change to admission policy lands in one place for both doors.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# ADR 0051 — Full session transcripts in the BFG corpus (with transcript-grade scrubbing)
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-13
|
|
4
|
+
**Context:** GDS-V4, task [#1004](https://example.com/builders#/task/1004) (full-transcript capture) with hard precondition task [#1003](https://example.com/builders#/task/1003) (transcript-grade scrubber, extends [#723](https://example.com/builders#/task/723)) and coverage task [#965](https://example.com/builders#/task/965). **Amends the privacy/size posture of [ADR 0027](<redacted>.md)** (BFG session-inefficiency evaluator) and its schema (`migrations/062_session_records.sql`). Companion to [ADR 0026](<redacted>.md) (BFG charter + §6C transparency), [ADR 0020](<redacted>.md) (security-model posture), [ADR 0028](<redacted>.md), and [ADR 0016](<redacted>.md) (read access is server-enforced, not a doc claim). Read API: tasks [#541](https://example.com/builders#/task/541)/[#627](https://example.com/builders#/task/627). Coverage history: [#551](https://example.com/builders#/task/551)/[#608](https://example.com/builders#/task/608)/[#617](https://example.com/builders#/task/617)/[#620](https://example.com/builders#/task/620).
|
|
5
|
+
**Status:** Proposed. Directive accepted (Lars, 2026-06-13); the **mechanism awaits review, and capture MUST NOT ship until the scrubber precondition (task 1003) is verified.**
|
|
6
|
+
**Track:** `internal` (dev-system / BFG).
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
[ADR 0027](<redacted>.md) made a deliberate choice, stated in `migrations/062_session_records.sql`:
|
|
11
|
+
|
|
12
|
+
> *"the heavy transcript CONTENT is never uploaded. Each row holds per-turn METADATA (role, tool name, error flag, token usage, content length) + short secret-scrubbed snippets + token aggregates. That keeps rows small (KB, not MB) and the secret surface minimal."*
|
|
13
|
+
|
|
14
|
+
That was the right call for an *efficiency metrics* evaluator. But it has a cost that surfaced concretely on 2026-06-13 while investigating why builder `IAMMASTERQUA` (id 25, Thetes) could not use the sandbox: pulling their 8 sessions from the corpus, the **assistant turns came back near-empty**. The corpus can compute ceremony-vs-work ratios and tool-error counts, but **neither the BFG nor an Archon can read what actually happened** — what the builder asked, what guidance they were given, where they hit a dead-end. The digest measures sessions; it cannot be *read*.
|
|
15
|
+
|
|
16
|
+
Lars's directive (2026-06-13): **the BFG must hold the full transcript of every session, readable — with secrets still scrubbed (non-negotiable).**
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
**Store the full, secret-scrubbed transcript of every session in the corpus, and reaffirm that read access is Archon/BFG-only + audited.** Four parts:
|
|
21
|
+
|
|
22
|
+
### 1. Full per-turn content (replaces the thin snippet model)
|
|
23
|
+
Capture the complete per-turn transcript — user prompts, assistant text, tool calls **and** their results — instead of length-bounded snippets. Stored as scrubbed text on each turn in `session_records` (new `transcript jsonb` column, or a widened `detail`; migration **085** reserved on claim 762). The per-turn *metadata* and `metrics` aggregates ADR 0027 relies on are kept — the evaluator logic is unchanged; only the stored fidelity grows.
|
|
24
|
+
|
|
25
|
+
### 2. Every session, no silent gaps
|
|
26
|
+
Coverage is already most of the way there: the `SessionEnd` hook (`scripts/gds/session-digest.js`) fires once per session end (not just on ship) and upserts **one record per `session_id`**, and `ship.js` covers the mid-session tail. This ADR keeps that invariant and pairs it with the coverage-reconciliation work (task 965) + the durable skip-logging (task 617) so a session that fails to upload is *visible*, never silently absent.
|
|
27
|
+
|
|
28
|
+
### 3. Hard precondition — transcript-grade scrubbing (task 1003)
|
|
29
|
+
Because the heavy content now leaves the box, the scrubber becomes load-bearing. Extend the secret-scrub in `scripts/gds/ship.js` + `scripts/gds/session-digest.js` (completing the still-open token-redaction task 723) to aggressively redact: API keys, GDS / Anthropic / Apple tokens, the contents of `~/.config/otb/gds-session.json` and `env`, `Authorization` headers, private keys, and high-entropy strings. **Scrubbing runs on the builder's box, before anything is uploaded.** A planted-secret test corpus must be caught in full before capture ships. **This task gates task 1004.**
|
|
30
|
+
|
|
31
|
+
### 4. Access + retention posture (reaffirmed and bounded)
|
|
32
|
+
- **Read access unchanged and reaffirmed:** full transcripts are sensitive. A builder may read their **own** sessions; any cross-builder read stays **Archon / BFG-principal only and fail-closed audited** (`/sessions/:id`, `/sessions/search` — the audit row is written *before* the query runs). This is server-enforced (ADR 0016), not a doc promise.
|
|
33
|
+
- **Retention — keep forever** (Lars, 2026-06-13: *"keep forever; we will optimize storage costs in the future"*). Full content is retained indefinitely in the central corpus; rows grow from KB to MB and the nightly `pg_dump` (task [#412](https://example.com/builders#/task/412)) grows with them. Storage-cost optimization (compression, cold-tiering, or off-DB object storage) is an explicit **future** concern, filed for later — not designed in now.
|
|
34
|
+
- **Server-side system of record, never on the boxes** (Lars, 2026-06-13: *"keep these on our server not on every dev box"*). The authoritative corpus is `session_records` in the central Postgres on the droplet. Dev boxes do **not** store, accumulate, or sync transcripts — a box scrubs its session at source and uploads it, holding only Claude Code's own transient local `.jsonl` (which we neither persist nor replicate across boxes). The home of record is single, and the standing secret surface on any individual box stays minimal.
|
|
35
|
+
- **Transparency:** builders should know their full sessions are retained server-side for BFG. Surface it in the primer / per ADR 0026 §6C's transparent-correction posture.
|
|
36
|
+
|
|
37
|
+
## The security trade-off (stated plainly)
|
|
38
|
+
|
|
39
|
+
This **reverses ADR 0027's central safety margin** ("content never uploaded"). The replacement safety model is three-layered: content is (a) **aggressively scrubbed at the source** before it leaves the box, (b) **access-gated** to Archon/BFG + audited, and (c) **centralized on the server**, never spread across dev boxes. Note this model deliberately does *not* lean on retention bounding — Lars chose to keep transcripts forever — so (a) the source scrub and (b) the access gate carry the full weight. The residual risk is real and acknowledged: a scrubber miss now leaks actual content rather than a short snippet, and it leaks it permanently — which is exactly why task 1003 (scrubber + planted-secret test) is sequenced **first** and is a hard gate on task 1004. The forever-corpus also becomes a higher-value target; the existing nightly backup and any future restore drill inherit that sensitivity.
|
|
40
|
+
|
|
41
|
+
## Consequences
|
|
42
|
+
|
|
43
|
+
- **Pros:** the BFG (and an Archon) can finally *read* sessions — root-causing builder friction like the IAMMASTERQUA sandbox dead-end becomes possible; "honest measurement" (a GDS-V4 goal) gets the raw material it was missing.
|
|
44
|
+
- **Cons / risks:** an ever-growing DB + backups (forever retention); a bigger, *permanent* secret-exposure surface if scrubbing regresses; full records of builders' work stored centrally (mitigated by scrub-at-source, audited gating, server-side centralization, and transparency — but explicitly **not** by retention limits).
|
|
45
|
+
- **Supersedes** the ADR 0027 / migration 062 *storage* note only. ADR 0027's evaluator design and read-gating posture stand.
|
|
46
|
+
|
|
47
|
+
## Alternatives considered
|
|
48
|
+
|
|
49
|
+
- **Fuller snippets, not full transcripts** — smaller blast radius, but does not satisfy the directive (assistant reasoning/answers still truncated).
|
|
50
|
+
- **Full transcripts, unscrubbed** — rejected outright: violates the non-negotiable secret constraint and our security posture.
|
|
51
|
+
- **Store transcripts off-DB (object storage), pointer in the row** — keeps rows small and isolates the blast radius; deferred as a future option since it would fork the existing one-record-per-session read API. Revisit if DB size becomes the binding constraint.
|
|
52
|
+
|
|
53
|
+
## Decisions recorded (Lars, 2026-06-13)
|
|
54
|
+
|
|
55
|
+
- **Retention: keep forever.** Full transcript content is never aged out; storage-cost optimization is deferred to a future task, not built now.
|
|
56
|
+
- **Storage location: central server only.** The corpus lives in the droplet's Postgres (`session_records`); dev boxes never accumulate or sync transcripts (see Decision §4).
|
|
57
|
+
- **Consequence for sequencing:** with no retention bound to limit exposure, the source-scrub (task 1003) and the audited access gate are the *only* safety layers. Task 1003 is therefore a hard, non-skippable precondition for task 1004, and its planted-secret test must pass before any capture code ships.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# ADR 0052 — Sandbox for everyone: a game-only preview entrypoint + the game in the starter clone
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-13
|
|
4
|
+
**Context:** GDS-V3, task [#1001](https://example.com/builders#/task/1001) (clone-scope + ungate) and task [#1002](https://example.com/builders#/task/1002) (hardware-verify). Builds on [ADR 0044](<redacted>.md) (the per-box live game preview / "sandbox") and [ADR 0031](<redacted>.md) (cloud dev boxes + rank-scoped source). Companion to [ADR 0046](<redacted>.md) (sandbox-first review) and [ADR 0016](<redacted>.md) (authority is server-enforced — source visibility grants nothing). **Reverses the full-clone gate from task [#903](https://example.com/builders#/task/903).** Decision by Lars, 2026-06-13.
|
|
5
|
+
**Status:** Accepted. Mechanism implemented under task [#1001](https://example.com/builders#/task/1001) (game-only entry `src/preview-server.js` + shared `registerGameRooms()` helper, `STARTER_SPARSE_PATHS` widened by `src/world`/`src/rooms`/`public`, `box-game-preview.service` repointed at the game-only entry, `box-source-fetch.sh` ungated); **hardware-verified under task [#1002](https://example.com/builders#/task/1002) (2026-06-14)** — a fresh **Xenos starter-scope** box (`LarsCode`) served the playable game over the public tunnel at `sandbox-<login>.example.com` (HTTP 200, Phaser client + `/game/*` assets) while **(b)** `/api/gds/*` returned 404 through that host (game-only entry does not mount the internal API) and **(c)** `migrations/` was absent on the box (the game paths `src/world`/`src/rooms`/`public` were present). Box deprovisioned after the run.
|
|
6
|
+
**Track:** `internal` (dev boxes / builder experience).
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
ADR 0044's sandbox (`sandbox-<login>.example.com`) only works on a Metic+ **full-clone** box. A starter (Xenos/Thetes) box can't serve it, for two reasons found in the code:
|
|
11
|
+
|
|
12
|
+
1. **The game isn't on the box.** `STARTER_SPARSE_PATHS` in `src/bongos/box-access.js` deliberately *excludes* the game — `src/world`, `src/rooms`, and the `public/` client are the "crown jewels" it protects.
|
|
13
|
+
2. **`server.js` can't boot game-only.** `server.js` top-level-requires `./src/bongos/routes` and `./src/rooms/*` and mounts `/api/gds` — so `box-game-preview.service` (which runs `node /workspace/server.js`) cannot start without the full internal stack present.
|
|
14
|
+
|
|
15
|
+
Task 903 therefore gated the preview on full-clone source. The concrete symptom (2026-06-13): builder `IAMMASTERQUA` (Thetes) — `curl localhost:3000` and the sandbox come up empty, with no explanation. Lars's directive: **the sandbox must work for all ranks.**
|
|
16
|
+
|
|
17
|
+
## The finding that shaped the design
|
|
18
|
+
|
|
19
|
+
Investigating the clone scope flipped our initial framing. A starter box **already carries the build tooling** — `scripts/`, `.claude/`, `src/bongos` (the GDS lib + API client; no secrets), `infra/`, `docs/`, `art/` — by deliberate choice (a 2026-06-07 note in `box-access.js`: *"usability wins so newcomers can actually interface with the builder skills"*; they need that tooling to run `/builder-*` at all). What a newcomer box lacks is the **game**. And per ADR 0016 authority is server-enforced, so reading source grants no privilege.
|
|
20
|
+
|
|
21
|
+
So the privacy boundary that actually matters for a **public** per-box URL is **runtime, not source**: `server.js` also serves `/api/gds`, and the sandbox tunnel points at the whole server — so today `sandbox-<login>.../api/gds/*` is reachable (degraded) on full boxes. Lars's decision (2026-06-13): **runtime-only gating** — add the game everywhere, serve the sandbox game-only, and leave the existing source posture as-is (source-tightening, idea [#213](https://example.com/builders#/idea/213), stays deferred).
|
|
22
|
+
|
|
23
|
+
## Decision
|
|
24
|
+
|
|
25
|
+
Make the sandbox available on every box via two changes, keeping the source posture unchanged.
|
|
26
|
+
|
|
27
|
+
### 1. Game in the starter clone
|
|
28
|
+
Add the product/game paths the preview needs — `src/world`, `src/rooms`, `public/` — to `STARTER_SPARSE_PATHS` (`src/bongos/box-access.js`). `migrations/` stays excluded (DB schema; a degraded-DB preview doesn't need it). Every box can now materialize the game.
|
|
29
|
+
|
|
30
|
+
### 2. A game-only preview entrypoint (the heart of "keep internals private")
|
|
31
|
+
Add a dedicated, minimal game-only server entry (e.g. `src/preview-server.js`) that serves **only**:
|
|
32
|
+
- `express.static(public/)` — the game client, and
|
|
33
|
+
- the **game** Colyseus rooms (`WorldRoom` + `QueueRoom`) over the WS transport, plus
|
|
34
|
+
- the minimal **game** HTTP endpoints the client needs to bootstrap (identity/queue/world — the implementer ports these from `server.js`'s non-`/api/gds`, non-`/builders` wiring).
|
|
35
|
+
|
|
36
|
+
It explicitly does **not** mount `/api/gds`, the `BuildersHallRoom`, the Discord bot, the uptime poller, or the status-host routing. `box-game-preview.service` runs **this** entry instead of `server.js`. To avoid the two entrypoints drifting on the room list, factor the game-room registration into a shared `registerGameRooms(gameServer)` helper that both `server.js` and the preview entry call.
|
|
37
|
+
|
|
38
|
+
Consequences of this split:
|
|
39
|
+
- A box never needs to boot the full stack to show the game; the game-only entry has no `src/bongos` dependency, so the public sandbox never serves the internal API even though `src/bongos` is present on the box.
|
|
40
|
+
- It **closes the current runtime exposure** where `sandbox-<login>.../api/gds/*` is reachable on full boxes — a net security improvement.
|
|
41
|
+
- `server.js` (the prod, full-stack entry) is left untouched — no rank-conditional behavior bolted onto the sensitive shared file.
|
|
42
|
+
|
|
43
|
+
### 3. Ungate the preview (reverse task 903)
|
|
44
|
+
`infra/box-source-fetch.sh` enables `box-game-preview.service` on **every** box, not only full-clone ones, now that the game paths are in the starter clone and the game-only entry boots without `src/bongos`.
|
|
45
|
+
|
|
46
|
+
## Why game-only entrypoint over a conditional mount
|
|
47
|
+
|
|
48
|
+
The alternative was to make `server.js` lazily/conditionally mount `/api/gds` (guard the `require`, skip on a box). **Rejected:** `server.js` is sensitive shared infra; adding rank-conditional runtime behavior there is the riskier path — a wrong guard mounts the internal API on a public per-box URL — and it ships `server.js`'s full wiring to every box. A separate, minimal, auditable game-only entry has a tiny fixed surface that *cannot* expose `/api/gds`. The only cost — keeping two game-room registrations in sync — is neutralized by the shared `registerGameRooms()` helper.
|
|
49
|
+
|
|
50
|
+
## Security posture
|
|
51
|
+
|
|
52
|
+
- **Source unchanged.** Newcomers keep the tooling source they already have, plus the game. The game client already ships to every browser; the game server (`src/world`/`src/rooms`) is product code, not permission/secret code. Authority stays server-enforced (ADR 0016) — a starter box reading game source gains nothing.
|
|
53
|
+
- **Runtime tightened.** The public sandbox serves game-only on every box; `/api/gds` and the BuildersHall are no longer reachable via `sandbox-<login>`. This is strictly safer than today.
|
|
54
|
+
- **Degraded-DB unchanged** (ADR 0044): no Postgres on the box; the game-only entry runs DB-less exactly as the preview does today.
|
|
55
|
+
|
|
56
|
+
## Consequences
|
|
57
|
+
|
|
58
|
+
- Every builder, any rank, gets a working live sandbox — fixes the `IAMMASTERQUA` dead-end and lets newcomers actually see their work.
|
|
59
|
+
- New small artifact (the game-only entry) + a shared room-registration helper to prevent drift; `box-game-preview.service` `ExecStart` changes; `STARTER_SPARSE_PATHS` widens by three game paths; task 903's gate is removed.
|
|
60
|
+
- **Verification (task 1002):** provision a real starter-scope (Xenos/Thetes) box and confirm (a) `sandbox-<login>` serves the playable game, (b) `sandbox-<login>.../api/gds/*` is **not** reachable, and (c) `migrations/` (and anything else still gated) is absent on the box.
|
|
61
|
+
|
|
62
|
+
## Alternatives considered
|
|
63
|
+
|
|
64
|
+
- **Conditional `/api/gds` mount in `server.js`** — rejected (above): riskier on a sensitive shared file.
|
|
65
|
+
- **Prebuilt game artifact, no source** — rejected per Lars's "just the game" choice; also breaks edit-then-preview.
|
|
66
|
+
- **Full clone for every box** — rejected: pulls `migrations/` and everything with no benefit; source-privacy is moot given server-enforced authority, so there's no reason to widen beyond the game.
|
|
67
|
+
|
|
68
|
+
## Follow-up (out of scope here)
|
|
69
|
+
|
|
70
|
+
Finer-grained source tightening — removing `scripts`/`.claude`/`src/bongos` from newcomer boxes (idea [#213](https://example.com/builders#/idea/213)) — remains deferred. Lars chose runtime-only gating for this change; tightening source would require a separate tooling-delivery design so newcomers can still run `/builder-*`.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ADR 0053 — Scoped, short-lived dev-box session + apex-cookie strip at the tunnel
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-13
|
|
4
|
+
**Context:** GDS, security reports SR[#2](https://example.com/builders#/task/2) (HIGH) + SR[#11](https://example.com/builders#/task/11) (HIGH) from the 2026-06-08 server audit ([#870](https://example.com/builders#/task/870)); task [#919](https://example.com/builders#/task/919). Owner decision 2026-06-09: do the proper redesign, not the interim named-tunnel disable. Governed by the trust-boundary rule in [ADR 0016](<redacted>.md) and the box subsystem in [ADR 0031](<redacted>.md) / [ADR 0038](<redacted>.md) / [ADR 0044](<redacted>.md).
|
|
5
|
+
**Status:** Accepted.
|
|
6
|
+
**Track:** `internal` (development system / builder dev boxes + access control).
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
A dev box is **outside** the ADR 0016 trust boundary — the GDS authenticates against the box, never the reverse. Two channels nonetheless put a builder's **full-authority** GDS bearer onto the box:
|
|
11
|
+
|
|
12
|
+
- **SR[#2](https://example.com/builders#/task/2) — baked full token.** `scripts/gds/box.js` mints a session and bakes it into cloud-init → `/root/.config/otb/gds-session.json`, so `box-source-fetch` can clone the rank-scoped repo on first boot (the [#818](https://example.com/builders#/task/818) chicken-and-egg: `/builder-setup` lives in the un-cloned repo). That session was `source='cli'`, and `source` was **purely informational** — `auth.requireBuilder` had no scope concept, so the baked token could claim/grade/ship and hit every `requireBuilder` route at the builder's live rank. Any on-box process, or the `169.254.169.254` metadata endpoint, could read it and act as the full builder.
|
|
13
|
+
- **SR[#11](https://example.com/builders#/task/11) — apex cookie rides the tunnel.** The hall session cookie is `gds_session; Domain=.example.com` (apex-scoped so [#726](https://example.com/builders#/task/726) SSO works across `builders.example.com`). The browser therefore sends it to `term-<login>.example.com` (ttyd) and `sandbox-<login>.example.com` (game preview) — both fronted by the box. The builder's full hall bearer rides the tunnel onto the less-trusted box.
|
|
14
|
+
|
|
15
|
+
## Decision
|
|
16
|
+
|
|
17
|
+
**Two independent defenses; the box never holds, and never receives, a full-authority credential.**
|
|
18
|
+
|
|
19
|
+
### 1. A third session source, `'box'`, that is deny-by-default (fixes SR[#2](https://example.com/builders#/task/2))
|
|
20
|
+
|
|
21
|
+
- `box.js` mints the baked session as **`source='box'`** (migration 090 widens the `builder_sessions.source` CHECK to `('cli','web','box')` and retroactively flips already-baked `user_agent='box-provision'` rows in place).
|
|
22
|
+
- `auth.requireBuilder` **rejects a `source='box'` session on every route** (`403 box_scope`) **except** the handful the on-box crons need, which opt in via a one-line `auth.allowBoxScope` middleware mounted ahead of `requireBuilder`. The allow-list IS the complete box surface:
|
|
23
|
+
|
|
24
|
+
| Route | On-box caller |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `GET /box/source-access` | `box-source-fetch.sh` (cron */10) |
|
|
27
|
+
| `GET /box/authorized-keys` | `<redacted>.sh` (cron */10) |
|
|
28
|
+
| `POST /box/heartbeat` | `box-heartbeat.sh` (cron */5) |
|
|
29
|
+
| `POST /box/terminal` | `box-report-terminal.sh` (quick-tunnel credsync) |
|
|
30
|
+
|
|
31
|
+
- **Fail closed:** a route *without* `allowBoxScope` denies a box session — so any route added later is automatically out of reach of a box token. No path-prefix matching (which drifts); the gate keys on an explicit per-route flag.
|
|
32
|
+
- A builder who wants to do real work **from** the box runs `/builder-reauth` (interactive device flow), minting a normal `cli`/`web` session exactly like any laptop — never auto-baked.
|
|
33
|
+
- The credential is still 24h refresh-on-use (unchanged since [#400](https://example.com/builders#/task/400); SR[#2](https://example.com/builders#/task/2)'s "non-expiring" predates that). The fix is **scope**, not lifetime: even a captured, still-valid box token can do nothing but bootstrap.
|
|
34
|
+
|
|
35
|
+
### 2. Strip the `Cookie` request header at the edge for box subdomains (fixes SR[#11](https://example.com/builders#/task/11))
|
|
36
|
+
|
|
37
|
+
- A **zone Transform Rule** (`http_request_late_transform` phase, action `rewrite` → `headers.Cookie.operation = remove`) removes the Cookie request header where `starts_with(http.host, "term-")` or `starts_with(http.host, "sandbox-")`, applied at the Cloudflare edge **before** the request enters the tunnel. ttyd uses basic-auth and the sandbox serves the game, so neither needs the cookie. One wildcard-prefix rule covers every builder's box.
|
|
38
|
+
- Implemented as `CfTunnelApi.ensureCookieStripRule` (idempotent GET-merge-PUT that preserves other transform rules), wired **best-effort** into `ensureNamedTunnel` so a token lacking the scope warns but never blocks provisioning.
|
|
39
|
+
- We do **not** narrow the apex cookie — that would break [#726](https://example.com/builders#/task/726) cross-subdomain SSO.
|
|
40
|
+
|
|
41
|
+
## Alternatives considered
|
|
42
|
+
|
|
43
|
+
- **Single-use enrollment code exchanged on first boot for the scoped token** (instead of baking a session). Cleaner provenance, but adds a stateful exchange + a first-boot failure mode; the baked-but-scoped session reuses the existing `createSession` path and the same exposure class as the already-baked SSH keys / tunnel token. Deferred — `source='box'` already removes the authority that made the bake dangerous.
|
|
44
|
+
- **A dedicated `scope` column** rather than overloading `source`. Rejected: the audit specified `source='box'`, and `source` already means "how was this minted." One column, one migration.
|
|
45
|
+
- **Path-prefix allow-list inside `requireBuilder`** (`/api/gds/box/...`). Rejected as fragile (prefix drift, and it would wrongly admit non-bootstrap `/box/*` routes like `/box/ssh-key`, `/box/ensure`, `GET /box/terminal`). The per-route opt-in flag is precise and fails closed.
|
|
46
|
+
- **A Cloudflare Worker** to delete the cookie. Unnecessary — Cloudflare Transform Rules permit *removing* (not setting) the `Cookie` request header, which is exactly our need.
|
|
47
|
+
- **Box-side cookie strip** (ttyd / origin). Rejected: the box is the untrusted party; the strip must be at the edge.
|
|
48
|
+
|
|
49
|
+
## Consequences
|
|
50
|
+
|
|
51
|
+
- A leaked or compromised dev-box credential can do **nothing but the 4 bootstrap endpoints** — no claiming, shipping, grading, or reading the builder surface.
|
|
52
|
+
- `hasCliSession` (keyed on `source='cli'`) no longer auto-ticks the onboarding "Run /builder-setup" step purely from provisioning — more honest; it is display-only and never gates graduation.
|
|
53
|
+
- `revokeAllSessionsForBuilder` (demotion/offboard) revokes `box` rows like any other → the clawback is unaffected.
|
|
54
|
+
- **Operator action:** the cookie-strip rule needs the `CLOUDFLARE_API_TOKEN` to carry zone **Transform Rules (Account Rulesets) edit** scope. If absent, provisioning still succeeds and logs a warning; apply the rule once in the CF dashboard or add the scope. Tracked as an owner blocker.
|
|
55
|
+
- Verified by `tests/box_scope_session.mjs` (the scope gate, in the CI unit gate) and `tests/cf_tunnel.mjs` (the edge-rule shape). Hardware verification of a fresh box boot under the box-scoped token is a follow-up ([#919](https://example.com/builders#/task/919) note).
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# ADR 0054 — Flat cost-plus session token reward (drachmae as equity)
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-13
|
|
4
|
+
**Context:** GDS-V4, task [#1005](https://example.com/builders#/task/1005). Builds on [ADR 0023](<redacted>.md) (per-task kind-multiplied credits), [ADR 0028](<redacted>.md) (the `llm-pricing.js` token→USD table + session-cost capture), and [ADR 0027](<redacted>.md) (the `session_records` upload-on-ship path this reward rides). Complements — does **not** replace — the per-task `credits_reward × kind_multiplier` path. Related future work: the full-transcript capture ADR (planned, task [#1004](https://example.com/builders#/task/1004)) will give this reward a more trustworthy raw-usage source, and the token-feed hardening scoped in task [#1005](https://example.com/builders#/task/1005) itself.
|
|
5
|
+
**Status:** Accepted.
|
|
6
|
+
**Track:** `internal` (development system / methodology).
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
We are about to onboard the first **incentivized** builders. Today the only payout is per-task `credits_reward` — a unitless number an author sets at task-creation time. It rewards declaring a big number, not doing expensive, real work. The owner's decision (2026-06-13) is to pay builders proportionally to the **true economic cost** of the work they did — the API token spend of the session — plus a margin, with drachmae standing in as **equity** in the project. It must go live today, be idempotent and reversible, and be impossible to inflate with a client-sent dollar figure.
|
|
11
|
+
|
|
12
|
+
## Decision
|
|
13
|
+
|
|
14
|
+
Per **shipped session** (the unit; per-task attribution is a later refinement — see the future-review trigger), grant a flat cost-plus reward:
|
|
15
|
+
|
|
16
|
+
drachmae = round(true_cost_usd × (1 + REWARD_MARGIN_PCT/100) × 100) // integer cents
|
|
17
|
+
REWARD_MARGIN_PCT = 20
|
|
18
|
+
|
|
19
|
+
- **`true_cost_usd` is computed SERVER-SIDE** from the per-model token counts the ship upload already carries (`session_records.model_usage`), priced via `src/bongos/llm-pricing.js` (the locked API list-price table). The client uploads token *counts*, never a dollar figure — that figure is never trusted from the client.
|
|
20
|
+
- **`true_cost` is deliberately the API LIST price**, not the builder's discounted subscription cost. A builder on a flat Claude Code subscription obtained those tokens far below list, so paying list-cost + 20% routes the subscription-vs-list spread to the builder as profit *on top of* the margin. This is intended: it is how the reward funds the builder's time and ideas, and it rewards taking advantage of the subsidy.
|
|
21
|
+
- **Margin is a code constant** (`REWARD_MARGIN_PCT` in `src/bongos/db.js`), owner-changed via a commit, not a settings UI — the same review-trail rationale as `KIND_MULTIPLIERS` ([ADR 0023](<redacted>.md)). A future migration can promote it to a per-builder/per-version table if the economics ever need live tuning. No rank above Archon is introduced; the knob is an owner-scoped policy constant.
|
|
22
|
+
- **Idempotent per `session_id`**: a new `credit_log.reason = 'session.token_reward'` row carries a `session_id` column backed by a partial-unique index, plus a SELECT fast-path guard inside the upsert transaction. Re-uploading the same session (re-ship, retry) is a true no-op — no double payout.
|
|
23
|
+
- The existing per-task credit path is untouched. Both the token reward and per-task credits accrue to `builders.total_credits` via the existing `pms_apply_credit` trigger.
|
|
24
|
+
- **Drachmae = equity units** (owner framing). No special code — it is the interpretation of `total_credits`.
|
|
25
|
+
|
|
26
|
+
### Two drachma conventions, one ledger (the load-bearing tension)
|
|
27
|
+
|
|
28
|
+
We knowingly accept that a drachma now means two things on the same ledger: a *unitless author estimate* (the per-task path) and a *USD-cent of real cost + 20%* (this path). We accept this for MVP speed; a future version may reconcile them (re-denominate per-task credits in cents, or split the ledgers). This ADR records the inconsistency so it is a deliberate decision, not a latent bug.
|
|
29
|
+
|
|
30
|
+
## The residual "rewards waste" risk + the equity mitigation
|
|
31
|
+
|
|
32
|
+
A flat cost-plus reward pays **more** for a more expensive session — it can reward token *waste* (thrashing, re-reading, long unproductive loops) as much as productive spend. This was raised and explicitly accepted by the owner, for three reasons:
|
|
33
|
+
|
|
34
|
+
1. **PASS-gated by construction.** Only a *successful ship* uploads a session record, so a session that burned tokens and shipped nothing earns nothing.
|
|
35
|
+
2. **Drachmae are equity, not cash.** A builder who pads cost dilutes a pool they themselves hold; the incentive to waste is bounded by the fact that the payout currency is ownership in the thing being built. Early builders want their percentage to be worth something, so they self-police. Inflating the denominator hurts the inflator.
|
|
36
|
+
3. **The waste signal already exists.** The BFG session-inefficiency evaluator ([ADR 0027](<redacted>.md)) flags wasteful sessions from the same uploaded digest; a later refinement can gate or discount the reward by a quality/efficiency factor, moving from flat cost-plus to cost-plus-adjusted-for-value. The owner's stated direction is to move toward **market dynamics** (bidding) once the builder pool is genuinely multi-party.
|
|
37
|
+
|
|
38
|
+
## Consequences
|
|
39
|
+
|
|
40
|
+
- **Migration 090** adds `session_records.model_usage` + `session_records.token_reward_drachmae`, `credit_log.session_id` + the partial-unique index `uniq_credit_session_token_reward`, and bumps the migration counter.
|
|
41
|
+
- `llm-pricing.js` gains pure, tested `priceModelUsage` + `canonicalizeModelId`.
|
|
42
|
+
- `db.js` gains `REWARD_MARGIN_PCT`, `computeSessionTokenReward`, `awardSessionTokenReward`, and the transactional `upsertSessionRecordWithReward` (the route now calls it instead of the bare upsert).
|
|
43
|
+
- `scripts/gds/ship.js` accumulates per-model usage into the digest; `POST /api/gds/sessions` accepts `model_usage`, prices it, and returns `token_reward` on the 201.
|
|
44
|
+
- The award is **append-only and reversible**: a compensating negative `credit_log` row reverses any reward without mutating history; the `total_credits` trigger nets it out automatically.
|
|
45
|
+
- First incentivized builders can onboard tomorrow with no further code.
|
|
46
|
+
|
|
47
|
+
## The future-review trigger
|
|
48
|
+
|
|
49
|
+
Revisit when: (a) per-task token attribution lands (the unit becomes the task, not the session); (b) the grader/BFG efficiency score is wired into the reward (cost-plus → value-adjusted); (c) the two-drachma-conventions tension is reconciled into one unit; or (d) the builder pool becomes multi-party enough to move to market/bidding pricing.
|
|
50
|
+
|
|
51
|
+
## Amendment — 2026-06-15 (task [#1138](https://example.com/builders#/task/1138)): 1 drachma = $1
|
|
52
|
+
|
|
53
|
+
**Trigger (c), partial.** An audit of the first incentivized builder's ledger (owner request) found the cents convention had inflated the balance ~100× (one builder, 31 sessions → 295,218 drachmae ≈ $2,952 of cost-plus) and that summing cents-denominated token rewards with the unitless per-task credits made the total incoherent. The owner's decision: **re-denominate the token reward so 1 drachma = $1**, not 1 cent.
|
|
54
|
+
|
|
55
|
+
**Change to the Decision formula** (supersedes the `× 100` in the formula above):
|
|
56
|
+
|
|
57
|
+
drachmae = round(true_cost_usd × (1 + REWARD_MARGIN_PCT/100)) // whole drachmae, 1 drachma = $1
|
|
58
|
+
REWARD_MARGIN_PCT = 20 // unchanged
|
|
59
|
+
|
|
60
|
+
- The trailing `× 100` (cents) is **dropped**. A $100 true-cost session now mints `round(100 × 1.20) = 120` drachmae (was 12,000). A sub-$0.42 session now rounds to 0 (was ~50) — intended.
|
|
61
|
+
- `computeSessionTokenReward` in `src/bongos/db.js` changed accordingly; `tests/credit_grant.mjs` updated; `llm-pricing.js` (the token→USD table) is **unchanged** — only the cents→dollars scale factor moved.
|
|
62
|
+
- **History was re-denominated, not left mixed.** Per the "append-only, reversible" rule above, existing `session.token_reward` rows were **not mutated**; a single compensating `credit_log` row (`reason='redenominate.token_reward_dollars'`, `task_id` → [#1138](https://example.com/builders#/task/1138)) restates the running total from cents to dollars, and the `total_credits` trigger nets it automatically. The original cents rows remain as historical record; the `session_records.token_reward_drachmae` display mirror was divided by 100 to match. At amendment time builder `#1` (example-owner) was the only holder of token rewards, so this touched one builder.
|
|
63
|
+
- **The per-task `credits_reward` path (ADR 0023) is untouched** — it stays a unitless author estimate. The two-conventions tension (§"Two drachma conventions, one ledger") is now *narrower* (both paths read on a ~dollar scale) but not formally reconciled; trigger (c) remains open for a full reconciliation.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# ADR 0055 — Server-mediated branch publish (the dev box holds no GitHub credential)
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-06-13
|
|
4
|
+
**Context:** GDS, task [#1025](https://example.com/builders#/task/1025). A live "can I publish from a dev box?" test failed even after CI self-deploy ([#859](https://example.com/builders#/task/859) / [ADR 0042](<redacted>.md)) went live. Sits at the intersection of [ADR 0042](<redacted>.md) (ci deploy mode), [ADR 0053](0053-scoped-dev-box-session.md) (box-scoped session, [#919](https://example.com/builders#/task/919)), and the box on-ramp ([ADR 0038](<redacted>.md) browser-only builders, [ADR 0044](<redacted>.md), [ADR 0046](<redacted>.md) sandbox-first ship gate / [#927](https://example.com/builders#/task/927), which expects `/builder-ship` to run *on* a box). Governed by the trust boundary in [ADR 0016](<redacted>.md).
|
|
5
|
+
**Status:** Accepted.
|
|
6
|
+
**Track:** `internal` (development system / builder dev boxes + ship pipeline).
|
|
7
|
+
|
|
8
|
+
## Problem
|
|
9
|
+
|
|
10
|
+
A builder on a provisioned cloud dev box **cannot publish/ship** in `ci` deploy mode. Two independent, by-design controls combine to block it, and neither was reconciled when both shipped on 2026-06-13:
|
|
11
|
+
|
|
12
|
+
1. **The box's GDS session is box-scoped.** Per [ADR 0053](0053-scoped-dev-box-session.md) the baked token is `source='box'` — deny-by-default, reaching only 4 bootstrap endpoints. `/builder-start`, claim, grade, ship all return `403 box_scope`.
|
|
13
|
+
2. **The box has no GitHub push credential.** In `ci` mode, `ship.js ciLand()` publishes by `git push origin <branch>` + `gh pr create` + `gh pr merge --auto`. The box has a **credential-less HTTPS** origin (`box-source-fetch.sh` scrubs the clone token by design), no `gh` CLI, no `~/.git-credentials`, no `GH_TOKEN`. `git ls-remote` fails with exit 128.
|
|
14
|
+
|
|
15
|
+
CI self-deploy ([#859](https://example.com/builders#/task/859)) fixed only the **last** leg — Actions deploys `main`→prod, so no machine holds the droplet key. It did nothing for the two **earlier** legs (a usable session + getting the branch onto GitHub), and [ADR 0053](0053-scoped-dev-box-session.md) deliberately *tightened* the first. The dev-box vision assumes ship-from-box works; nobody wired the box for `ci`-mode publishing. The server itself holds **no** GitHub push credential today (the only GitHub-related secret is the droplet SSH key inside Actions for `deploy-prod.yml`).
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
**The box never holds a GitHub push credential. It uploads its branch to the GDS, and the server pushes it.**
|
|
20
|
+
|
|
21
|
+
### 1. The box re-auths first (the box-scope model is untouched)
|
|
22
|
+
|
|
23
|
+
A box-scoped token still cannot ship. The builder runs **`/builder-reauth`** (the existing UI-first browser device flow — [ADR 0053](0053-scoped-dev-box-session.md) §1) to mint a normal `cli` session. That real session — not the baked box token — authorizes the publish.
|
|
24
|
+
|
|
25
|
+
### 2. A new authenticated publish endpoint, NOT box-scoped
|
|
26
|
+
|
|
27
|
+
- `POST /api/gds/tasks/:id/publish-branch` — `auth.requireBuilder` + `gateTaskOwnership` (the same [#871](https://example.com/builders#/task/871) gate as `/grade`/`/confirm`/`/ship`), **deliberately without `auth.allowBoxScope`**, so a box-scoped session is `403 box_scope`. Accepts the branch as a **thin git bundle** (base64, `origin/main..HEAD`), `head_sha`, and `value_summary`. The server: writes the bundle to a per-request temp dir, fast `git clone --bare --local` of its own checkout for `main`'s objects (no network, no worktree touch → never races the `deploy-prod.yml` reset), unbundles, **verifies the bundle tip == the claimed `head_sha`** (binds the pushed code to the already-graded sha), then `git push` to GitHub via a **one-shot `x-access-token:<redacted> URL** (never persisted, never logged — the `box-source-fetch.sh` pattern), opens the PR, and enables auto-merge.
|
|
28
|
+
- `GET /api/gds/tasks/:id/publish-status?branch=…` — ownership-gated; the server derives PR + deploy state live from GitHub so `ship.js` confirms the land WITHOUT `gh` on the box. No DB column added.
|
|
29
|
+
- Implementation: [`src/bongos/github-push.js`](../../src/bongos/github-push.js) (push/PR/status via `execFile` arg-arrays + raw `fetch`, all behind test seams), routes in [`src/bongos/routes/tasks.js`](../../src/bongos/routes/tasks.js), the bundle body bypasses the global 64 KB JSON cap via the skip-list in [`src/bongos/routes.js`](../../src/bongos/routes.js) (route-scoped 32 MB parser + a 20 MB decoded-byte cap).
|
|
30
|
+
|
|
31
|
+
### 3. `ship.js` chooses the path automatically
|
|
32
|
+
|
|
33
|
+
`ciLand` splits into `ciLandLocal` (today's `git`+`gh`, byte-for-byte — laptop unchanged) and `ciLandServer` (bundle → `publish-branch` → poll `publish-status`). `pushVia()` picks: `OTB_PUSH_VIA_SERVER=1/0` overrides; otherwise a machine with an authenticated `gh` (laptop) is `local`, everything else (a dev box) is `server`. `doctor.js` reports the same path and no longer fails on missing `gh` when server-mediated.
|
|
34
|
+
|
|
35
|
+
### 4. The server credential
|
|
36
|
+
|
|
37
|
+
A **fine-grained GitHub PAT** scoped to the single repo: `Contents: read/write` + `Pull requests: read/write` + `Actions: read`, set as `GITHUB_PUSH_TOKEN` in the droplet EnvironmentFile ([ADR 0022](0022-secrets-policy.md)). It **cannot** push to branch-protected `main` and **cannot** bypass required checks — a leaked server token can only land code on a feature branch behind the same checks every PR faces. **Inert-safe:** with the token unset the endpoint returns `503 push_unconfigured` and laptops are unaffected.
|
|
38
|
+
|
|
39
|
+
> **Update (2026-06-13, task 1028 — GitHub App):** the preferred server credential is now a **GitHub App**, not a standing PAT. The server holds only the App private key (`GITHUB_APP_ID` + `GITHUB_APP_INSTALLATION_ID` + `GITHUB_APP_PRIVATE_KEY`) and mints a short-lived (~1h), repo-scoped **installation token** per push window — so there's no standing push secret at rest. `github-push.js resolveToken()` prefers the App and falls back to the PAT (`GITHUB_PUSH_TOKEN`) during cutover; `isConfigured()` is true if **either** is set, so the route's inert-safe `503` is unchanged. The App JWT is RS256-signed with `node:crypto` (no new dependency). Same single-repo scopes (Contents r/w + PR r/w + Actions read) and the same can't-touch-`main` guarantee. **LIVE on prod as of 2026-06-13 (task 1043 cutover, blocker 31 resolved):** the App (App ID 4047551, installation 140135657, `repository_selection=selected`) is installed and its key is set in `/etc/example/web-source.env`; verified that the server mints + uses an installation token (`publish-status` → 200, and a broken key fails loud as `503`/error rather than falling back). The old `GITHUB_PUSH_TOKEN` was **removed** — there is now **no standing push secret at rest**, only the App private key.
|
|
40
|
+
|
|
41
|
+
## Consequences
|
|
42
|
+
|
|
43
|
+
- A dev-box builder ships end-to-end: `/builder-reauth` → claim → edit → `/builder-stage` ([ADR 0046](<redacted>.md)) → `/builder-ship`, with the GDS doing the push. The box holds no GitHub credential, so the [ADR 0053](0053-scoped-dev-box-session.md) blast-radius argument still holds.
|
|
44
|
+
- The blast radius moves from "box holds a push credential" to "server holds a branch-push-only PAT." This is the direction blocker [#15](https://example.com/builders#/blocker/15) ([SEC #863](https://example.com/builders#/task/863)) already wants — a server identity holding the push credential, not builder machines.
|
|
45
|
+
- **Follow-up (hardening) — DONE + LIVE (task 1028 code, task 1043 cutover):** the standing PAT is replaced by a **GitHub App** (the server holds only the App private key and mints short-lived, repo-scoped installation tokens per push). See the *Update* above — the App is live on prod and the PAT has been removed.
|
|
46
|
+
- **Residual:** the server verifies the bundle tip == the claimed sha and that the caller holds the claim, but does not cheaply prove every commit in the bundle was authored under the claim — branch protection + required checks on `main` (which the credential cannot bypass) are the real gate, exactly as for any PR.
|
|
47
|
+
- **Operator action — DONE (blocker 31, resolved 2026-06-13):** the GitHub App was created and `GITHUB_APP_ID` + `GITHUB_APP_INSTALLATION_ID` + `GITHUB_APP_PRIVATE_KEY` set on the droplet (task 1043); `GITHUB_PUSH_TOKEN` then removed. To rotate the key: generate a new private key in the App's settings, replace `GITHUB_APP_PRIVATE_KEY` (single-line, literal `\n`) in `/etc/example/web-source.env`, restart `example`. To roll back to the PAT: restore `web-source.env.bak-task1043` and set a fresh `GITHUB_PUSH_TOKEN` (with the App vars absent, `resolveToken()` falls back to the PAT).
|