@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,318 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Discord role-sync — Phase 3 F5 (ADR 0033 §4).
|
|
4
|
+
//
|
|
5
|
+
// ONE-WAY: GDS → Discord. The bot sets a member's Discord rank-role
|
|
6
|
+
// (Xenos / Metic / Archon) from the LIVE `builders.rank`. It READS the GDS and
|
|
7
|
+
// WRITES Discord — never the reverse — so a Discord role can never feed
|
|
8
|
+
// authority back into the GDS (ADR 0016 / 0032 §2: roles are cosmetic
|
|
9
|
+
// reflections, never keys). Role badges become continuously honest.
|
|
10
|
+
//
|
|
11
|
+
// Fired on three triggers (ADR 0033 §4): on link (F2 callback), on rank change
|
|
12
|
+
// (PATCH /builders/:id/rank), and on a periodic reconcile (the bot's interval +
|
|
13
|
+
// on-ready sweep). All best-effort: a Discord-side failure (missing Manage
|
|
14
|
+
// Roles, hierarchy, member left the guild) logs and moves on — it must never
|
|
15
|
+
// break a rank change or a link.
|
|
16
|
+
//
|
|
17
|
+
// The FOUR live ranks have Discord roles (xenos/thetes/metic/archon). Any other
|
|
18
|
+
// rank value maps to NO managed role, and the sync strips the managed roles.
|
|
19
|
+
//
|
|
20
|
+
// Requires (go-live, not code): the GuildMembers privileged intent, the bot to
|
|
21
|
+
// hold Manage Roles, and the bot's own role positioned ABOVE the three rank
|
|
22
|
+
// roles in the guild hierarchy. Without those the role writes 403 and are
|
|
23
|
+
// logged — the GDS is unaffected.
|
|
24
|
+
|
|
25
|
+
const fs = require('node:fs');
|
|
26
|
+
const path = require('node:path');
|
|
27
|
+
const os = require('node:os');
|
|
28
|
+
const db = require('./db');
|
|
29
|
+
const ic = require('../../src/module-api');
|
|
30
|
+
|
|
31
|
+
// The live ranks that map to a Discord role badge = the full rank ladder, in
|
|
32
|
+
// order low→high. Sourced from db.LIVE_RANK_LADDER — the server-side single
|
|
33
|
+
// source (D1) — so a ladder change (e.g. thetes, ADR 0034) lands in one place.
|
|
34
|
+
const MANAGED_RANKS = db.LIVE_RANK_LADDER;
|
|
35
|
+
|
|
36
|
+
// PURE — the rank→role-id map from env (DISCORD_ROLE_XENOS/METIC/ARCHON) or
|
|
37
|
+
// ~/.config/otb/discord-bot.json `role_ids: { xenos, metic, archon }`. Returns
|
|
38
|
+
// only the keys actually configured; an empty map means role-sync is inert.
|
|
39
|
+
function loadRoleMap(env = process.env) {
|
|
40
|
+
const out = {};
|
|
41
|
+
const envKeys = { xenos: 'DISCORD_ROLE_XENOS', thetes: 'DISCORD_ROLE_THETES', metic: 'DISCORD_ROLE_METIC', archon: 'DISCORD_ROLE_ARCHON' };
|
|
42
|
+
for (const rank of MANAGED_RANKS) {
|
|
43
|
+
if (env[envKeys[rank]]) out[rank] = String(env[envKeys[rank]]);
|
|
44
|
+
}
|
|
45
|
+
if (Object.keys(out).length) return out;
|
|
46
|
+
try {
|
|
47
|
+
const raw = ic.readConfigFileSync('discord-bot.json');
|
|
48
|
+
const cfg = JSON.parse(raw);
|
|
49
|
+
if (cfg.role_ids && typeof cfg.role_ids === 'object') {
|
|
50
|
+
for (const rank of MANAGED_RANKS) {
|
|
51
|
+
if (cfg.role_ids[rank]) out[rank] = String(cfg.role_ids[rank]);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} catch (_) { /* not configured */ }
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// PURE — the guild id the bot manages roles in. Env DISCORD_GUILD_ID, else the
|
|
59
|
+
// file's guild_id, else null (caller falls back to the bot's single cached guild).
|
|
60
|
+
function loadGuildId(env = process.env) {
|
|
61
|
+
if (env.DISCORD_GUILD_ID) return String(env.DISCORD_GUILD_ID);
|
|
62
|
+
try {
|
|
63
|
+
const raw = ic.readConfigFileSync('discord-bot.json');
|
|
64
|
+
const cfg = JSON.parse(raw);
|
|
65
|
+
if (cfg.guild_id) return String(cfg.guild_id);
|
|
66
|
+
} catch (_) { /* not configured */ }
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// PURE — a rank's Discord role id, or null if it isn't a managed live rank or
|
|
71
|
+
// has no configured mapping.
|
|
72
|
+
function rankToRoleId(rank, roleMap) {
|
|
73
|
+
if (!MANAGED_RANKS.includes(rank)) return null;
|
|
74
|
+
return roleMap[rank] || null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// PURE — given the member's CURRENT role ids and their target rank, compute the
|
|
78
|
+
// minimal change: which managed role to add (the target, if missing) and which
|
|
79
|
+
// managed roles to remove (any other rank role they currently hold). Idempotent:
|
|
80
|
+
// a member already holding exactly their rank role yields { add:null, remove:[] }.
|
|
81
|
+
// Never touches non-managed roles — we only ever own the three rank badges.
|
|
82
|
+
function computeRoleChange(currentRoleIds, rank, roleMap) {
|
|
83
|
+
const managedIds = MANAGED_RANKS.map((r) => roleMap[r]).filter(Boolean);
|
|
84
|
+
const target = rankToRoleId(rank, roleMap);
|
|
85
|
+
const current = new Set((currentRoleIds || []).map(String));
|
|
86
|
+
const remove = managedIds.filter((id) => id !== target && current.has(id));
|
|
87
|
+
const add = target && !current.has(target) ? target : null;
|
|
88
|
+
return { add, remove };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// The bot's managed guild, or null if the bot isn't connected / not in it.
|
|
92
|
+
// Lazy-requires discord-bot to avoid a require cycle (bot → role-sync on its
|
|
93
|
+
// reconcile path; role-sync → bot here).
|
|
94
|
+
function getGuild() {
|
|
95
|
+
const bot = require('./discord-bot');
|
|
96
|
+
const client = bot.getClient && bot.getClient();
|
|
97
|
+
if (!client) return null;
|
|
98
|
+
const gid = loadGuildId();
|
|
99
|
+
return gid ? client.guilds.cache.get(gid) || null : client.guilds.cache.first() || null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const realDeps = {
|
|
103
|
+
getDiscordLink: db.getDiscordLink,
|
|
104
|
+
getBuilderById: db.getBuilderById,
|
|
105
|
+
listDiscordLinks: db.listDiscordLinks,
|
|
106
|
+
loadRoleMap,
|
|
107
|
+
getGuild,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Sync ONE builder's Discord rank-role from their live rank. Best-effort, never
|
|
111
|
+
// throws. Returns a small status object for logging/tests.
|
|
112
|
+
async function syncBuilderRole(builderId, deps = realDeps) {
|
|
113
|
+
try {
|
|
114
|
+
const link = await deps.getDiscordLink(builderId);
|
|
115
|
+
if (!link) return { ok: false, reason: 'not_linked' };
|
|
116
|
+
const builder = await deps.getBuilderById(builderId);
|
|
117
|
+
if (!builder) return { ok: false, reason: 'no_builder' };
|
|
118
|
+
|
|
119
|
+
const roleMap = deps.loadRoleMap();
|
|
120
|
+
if (!Object.keys(roleMap).length) return { ok: false, reason: 'no_role_map' };
|
|
121
|
+
|
|
122
|
+
const guild = deps.getGuild();
|
|
123
|
+
if (!guild) return { ok: false, reason: 'no_guild' };
|
|
124
|
+
|
|
125
|
+
const member = await guild.members.fetch(link.discord_user_id).catch(() => null);
|
|
126
|
+
if (!member) return { ok: false, reason: 'member_not_found' };
|
|
127
|
+
|
|
128
|
+
const current = [...member.roles.cache.keys()];
|
|
129
|
+
const { add, remove } = computeRoleChange(current, builder.rank, roleMap);
|
|
130
|
+
if (!add && !remove.length) return { ok: true, noop: true };
|
|
131
|
+
|
|
132
|
+
if (remove.length) await member.roles.remove(remove, 'GDS role-sync (ADR 0033 §4)');
|
|
133
|
+
if (add) await member.roles.add(add, 'GDS role-sync (ADR 0033 §4)');
|
|
134
|
+
return { ok: true, add, removed: remove };
|
|
135
|
+
} catch (err) {
|
|
136
|
+
// Most likely a Discord permission/hierarchy problem — log, don't throw.
|
|
137
|
+
console.error(`[discord-bot] role-sync failed for builder ${builderId}:`, err.message);
|
|
138
|
+
return { ok: false, reason: 'error', error: err.message };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Display colors for each managed rank role (Discord uses decimal integers).
|
|
143
|
+
// These mirror the hall badge palette in modules/hall-ui/public/style.css
|
|
144
|
+
// (.rank-badge--<rank> background) so a builder's Discord role color matches the
|
|
145
|
+
// badge they see in the builders' hall; 0 = Discord default if a rank is missing.
|
|
146
|
+
// xenos #d4ae7a (weathered bronze) thetes #c98a5e (polished copper)
|
|
147
|
+
// metic #e6e1d3 (weathered marble) archon #e7c14a (sun gold)
|
|
148
|
+
const RANK_COLORS = { xenos: 0xD4AE7A, thetes: 0xC98A5E, metic: 0xE6E1D3, archon: 0xE7C14A };
|
|
149
|
+
|
|
150
|
+
// Create any managed rank roles that are missing from the guild and save their
|
|
151
|
+
// ids back to ~/.config/otb/discord-bot.json. Called once on bot ready so the
|
|
152
|
+
// config stays self-maintaining — no manual copy-paste of role ids needed.
|
|
153
|
+
// Best-effort: a Discord 403 (missing Manage Roles) logs and returns the
|
|
154
|
+
// partial map rather than crashing the reconcile that follows. Env-sourced role
|
|
155
|
+
// ids are never overwritten (env wins over file per loadRoleMap convention).
|
|
156
|
+
async function ensureRankRoles(guild) {
|
|
157
|
+
if (!guild) return {};
|
|
158
|
+
const botToken = require('./auth').loadDiscordBotToken();
|
|
159
|
+
if (!botToken) return {};
|
|
160
|
+
|
|
161
|
+
// Load existing map; determine which ranks have no id yet.
|
|
162
|
+
const existing = loadRoleMap();
|
|
163
|
+
const missing = MANAGED_RANKS.filter((r) => !existing[r]);
|
|
164
|
+
|
|
165
|
+
const created = {};
|
|
166
|
+
for (const rank of missing) {
|
|
167
|
+
try {
|
|
168
|
+
const res = await fetch(`https://discord.com/api/guilds/${guild.id}/roles`, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: { Authorization: `Bot ${botToken}`, 'Content-Type': 'application/json' },
|
|
171
|
+
body: JSON.stringify({
|
|
172
|
+
name: rank.charAt(0).toUpperCase() + rank.slice(1),
|
|
173
|
+
color: RANK_COLORS[rank] || 0,
|
|
174
|
+
hoist: false,
|
|
175
|
+
mentionable: false,
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
if (!res.ok) {
|
|
179
|
+
const text = await res.text();
|
|
180
|
+
console.error(`[discord-bot] ensureRankRoles: failed to create ${rank} role (${res.status}): ${text}`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const role = await res.json();
|
|
184
|
+
created[rank] = role.id;
|
|
185
|
+
console.log(`[discord-bot] ensureRankRoles: created ${rank} role (id=${role.id})`);
|
|
186
|
+
} catch (e) {
|
|
187
|
+
console.error(`[discord-bot] ensureRankRoles: error creating ${rank} role:`, e.message);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (Object.keys(created).length) {
|
|
192
|
+
// Persist the new ids into the config file so the next boot finds them.
|
|
193
|
+
// File-only: we never overwrite env vars. If the file isn't writable, log
|
|
194
|
+
// and continue — the ids will be recreated on the next boot instead.
|
|
195
|
+
const cfgPath = ic.configPath('discord-bot.json');
|
|
196
|
+
try {
|
|
197
|
+
// Read the existing config from wherever it lives (configured or legacy
|
|
198
|
+
// dir) so we preserve its other fields, then write to the configured dir.
|
|
199
|
+
const cfg = JSON.parse(ic.readConfigFileSync('discord-bot.json') || '{}');
|
|
200
|
+
cfg.role_ids = Object.assign({}, cfg.role_ids || {}, created);
|
|
201
|
+
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 4), 'utf8');
|
|
202
|
+
console.log(`[discord-bot] ensureRankRoles: saved ${Object.keys(created).join(', ')} to config.`);
|
|
203
|
+
} catch (e) {
|
|
204
|
+
console.error('[discord-bot] ensureRankRoles: could not save role ids to config:', e.message);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const finalMap = Object.assign({}, existing, created);
|
|
209
|
+
|
|
210
|
+
// Reorder managed roles in Discord to match the rank ladder (MANAGED_RANKS is
|
|
211
|
+
// low→high; archon must sit above metic above thetes above xenos). Discord
|
|
212
|
+
// allows partial PATCH — we only send the 4 managed roles; others are untouched.
|
|
213
|
+
// Strategy: fetch all guild roles, collect the positions currently occupied by
|
|
214
|
+
// managed roles, sort them descending, and assign archon→metic→thetes→xenos
|
|
215
|
+
// from highest to lowest. This keeps managed roles in the same position band
|
|
216
|
+
// while making their relative order correct.
|
|
217
|
+
const managedIds = MANAGED_RANKS.map((r) => finalMap[r]).filter(Boolean);
|
|
218
|
+
if (managedIds.length >= 2) {
|
|
219
|
+
try {
|
|
220
|
+
const allRolesRes = await fetch(`https://discord.com/api/guilds/${guild.id}/roles`, {
|
|
221
|
+
headers: { Authorization: `Bot ${botToken}` },
|
|
222
|
+
});
|
|
223
|
+
if (allRolesRes.ok) {
|
|
224
|
+
const allRoles = await allRolesRes.json();
|
|
225
|
+
const posMap = {};
|
|
226
|
+
const colorMap = {};
|
|
227
|
+
for (const r of allRoles) { posMap[r.id] = r.position; colorMap[r.id] = r.color; }
|
|
228
|
+
|
|
229
|
+
// Recolor managed roles whose color has drifted from RANK_COLORS. Color
|
|
230
|
+
// is only set at creation (above), so a palette change here wouldn't
|
|
231
|
+
// otherwise reach roles that already exist — PATCH each one individually
|
|
232
|
+
// (the bulk roles endpoint only moves positions, not colors).
|
|
233
|
+
for (const rank of MANAGED_RANKS) {
|
|
234
|
+
const id = finalMap[rank];
|
|
235
|
+
const desired = RANK_COLORS[rank];
|
|
236
|
+
if (!id || desired === undefined || colorMap[id] === desired) continue;
|
|
237
|
+
try {
|
|
238
|
+
const recolorRes = await fetch(`https://discord.com/api/guilds/${guild.id}/roles/${id}`, {
|
|
239
|
+
method: 'PATCH',
|
|
240
|
+
headers: { Authorization: `Bot ${botToken}`, 'Content-Type': 'application/json' },
|
|
241
|
+
body: JSON.stringify({ color: desired }),
|
|
242
|
+
});
|
|
243
|
+
if (recolorRes.ok) {
|
|
244
|
+
console.log(`[discord-bot] ensureRankRoles: recolored ${rank} role to #${desired.toString(16).padStart(6, '0')}`);
|
|
245
|
+
} else {
|
|
246
|
+
const text = await recolorRes.text();
|
|
247
|
+
console.error(`[discord-bot] ensureRankRoles: recolor ${rank} failed (${recolorRes.status}): ${text}`);
|
|
248
|
+
}
|
|
249
|
+
} catch (e) {
|
|
250
|
+
console.error(`[discord-bot] ensureRankRoles: error recoloring ${rank} role:`, e.message);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Gather positions currently occupied by managed roles, sorted desc.
|
|
255
|
+
const managedPositions = managedIds
|
|
256
|
+
.map((id) => posMap[id])
|
|
257
|
+
.filter((p) => p !== undefined)
|
|
258
|
+
.sort((a, b) => b - a);
|
|
259
|
+
|
|
260
|
+
// MANAGED_RANKS is low→high, so reverse for highest→lowest assignment.
|
|
261
|
+
const highToLow = [...MANAGED_RANKS].reverse();
|
|
262
|
+
const patch = [];
|
|
263
|
+
for (let i = 0; i < highToLow.length; i++) {
|
|
264
|
+
const id = finalMap[highToLow[i]];
|
|
265
|
+
if (id && managedPositions[i] !== undefined) patch.push({ id, position: managedPositions[i] });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (patch.length >= 2) {
|
|
269
|
+
const patchRes = await fetch(`https://discord.com/api/guilds/${guild.id}/roles`, {
|
|
270
|
+
method: 'PATCH',
|
|
271
|
+
headers: { Authorization: `Bot ${botToken}`, 'Content-Type': 'application/json' },
|
|
272
|
+
body: JSON.stringify(patch),
|
|
273
|
+
});
|
|
274
|
+
if (patchRes.ok) {
|
|
275
|
+
const order = highToLow.filter((r) => finalMap[r]).join(' > ');
|
|
276
|
+
console.log(`[discord-bot] ensureRankRoles: reordered roles (${order})`);
|
|
277
|
+
} else {
|
|
278
|
+
const text = await patchRes.text();
|
|
279
|
+
console.error(`[discord-bot] ensureRankRoles: role reorder failed (${patchRes.status}): ${text}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
} catch (e) {
|
|
284
|
+
console.error('[discord-bot] ensureRankRoles: error reordering roles:', e.message);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return finalMap;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Sweep every linked builder. Used on-ready + on the periodic interval. Returns
|
|
292
|
+
// counts for the log line. Sequential to stay gentle on Discord's rate limits;
|
|
293
|
+
// the link table is tiny.
|
|
294
|
+
async function reconcileAll(deps = realDeps) {
|
|
295
|
+
const links = await deps.listDiscordLinks();
|
|
296
|
+
let synced = 0;
|
|
297
|
+
for (const l of links) {
|
|
298
|
+
const r = await syncBuilderRole(l.builder_id, deps);
|
|
299
|
+
if (r.ok) synced++;
|
|
300
|
+
}
|
|
301
|
+
return { total: links.length, synced };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
module.exports = {
|
|
305
|
+
// pure (unit-tested)
|
|
306
|
+
MANAGED_RANKS,
|
|
307
|
+
RANK_COLORS,
|
|
308
|
+
loadRoleMap,
|
|
309
|
+
loadGuildId,
|
|
310
|
+
rankToRoleId,
|
|
311
|
+
computeRoleChange,
|
|
312
|
+
// effectful
|
|
313
|
+
getGuild,
|
|
314
|
+
ensureRankRoles,
|
|
315
|
+
syncBuilderRole,
|
|
316
|
+
reconcileAll,
|
|
317
|
+
realDeps,
|
|
318
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"key": "discord",
|
|
3
|
+
"title": "Discord",
|
|
4
|
+
"description": "Outbound ship broadcast + the inbound #ideas/#bugs bot + role-sync + channel management + account-link state. Mirror only, never authority (ADR 0033).",
|
|
5
|
+
"coreVersion": "^1.4.0",
|
|
6
|
+
"default": false,
|
|
7
|
+
"contributes": {
|
|
8
|
+
"routes": ["discord", "bug-attachments"],
|
|
9
|
+
"pollers": ["discord-bot"],
|
|
10
|
+
"uiSections": []
|
|
11
|
+
},
|
|
12
|
+
"provides": ["discord.isLinked"],
|
|
13
|
+
"consumes": ["ideas.capture"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// modules/discord/pollers/discord-bot.js — the web-process poller entry the loader
|
|
2
|
+
// starts/stops (BV1.R47 / task 1416).
|
|
3
|
+
//
|
|
4
|
+
// The module manifest declares contributes.pollers:["discord-bot"]; the loader
|
|
5
|
+
// (src/module-loader/loader.js startModulePollers) requires THIS file and calls
|
|
6
|
+
// start(deps) at boot, stop() at shutdown — the module-agnostic successor to the
|
|
7
|
+
// hardcoded `discordBot.start()` block server.js used to carry. A thin shim over
|
|
8
|
+
// the bot itself: start() is non-blocking + failure-isolated by the loader (a
|
|
9
|
+
// Discord gateway problem must never take the game/API down), and stop() closes
|
|
10
|
+
// the gateway socket cleanly so a systemd restart leaves no zombie session.
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const bot = require('../discord-bot');
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
start: () => bot.start(),
|
|
18
|
+
stop: () => bot.stop(),
|
|
19
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/bongos/routes/bug-attachments.js — serve re-hosted #bugs screenshots (task
|
|
2
|
+
// #936, follow-on to ADR 0047).
|
|
3
|
+
//
|
|
4
|
+
// The bot downloads images posted in #bugs and re-hosts them here (durable —
|
|
5
|
+
// Discord CDN links expire). A bug-task's description embeds
|
|
6
|
+
// /api/bongos/bug-attachments/<file>, so when a builder opens the task in the hall
|
|
7
|
+
// the <img> loads from this route. The browser sends the gds_session cookie on
|
|
8
|
+
// the same-origin image request, so requireBuilder gates it: builders-only,
|
|
9
|
+
// exactly the access posture for the rest of the GDS surface.
|
|
10
|
+
//
|
|
11
|
+
// rank: the route is auth.requireBuilder — any authenticated builder may VIEW a
|
|
12
|
+
// bug screenshot (there's no rank-restricted content here; bugs are builder
|
|
13
|
+
// work). Writes happen only via the bot (bug-attachments.js), never this route.
|
|
14
|
+
|
|
15
|
+
const express = require('express');
|
|
16
|
+
const fs = require('node:fs/promises');
|
|
17
|
+
const auth = require('../auth');
|
|
18
|
+
const attach = require('../bug-attachments');
|
|
19
|
+
|
|
20
|
+
module.exports = function buildBugAttachmentsRouter() {
|
|
21
|
+
const router = express.Router();
|
|
22
|
+
|
|
23
|
+
// GET /api/bongos/bug-attachments/:name — stream one stored image.
|
|
24
|
+
router.get('/bug-attachments/:name', auth.requireBuilder, async (req, res) => {
|
|
25
|
+
const p = attach.resolveSafe(req.params.name);
|
|
26
|
+
if (!p) return res.fail('bad_name', 400);
|
|
27
|
+
try {
|
|
28
|
+
const data = await fs.readFile(p);
|
|
29
|
+
res.setHeader('Content-Type', attach.contentTypeForName(req.params.name));
|
|
30
|
+
// Never let a browser sniff these into something executable, and keep them
|
|
31
|
+
// inline (so they render in the hall) but explicitly not as a download.
|
|
32
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
33
|
+
res.setHeader('Content-Disposition', 'inline');
|
|
34
|
+
res.setHeader('Cache-Control', 'private, max-age=86400');
|
|
35
|
+
res.send(data);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (err.code === 'ENOENT') return res.fail('not_found', 404);
|
|
38
|
+
console.error('[gds] GET /bug-attachments', err);
|
|
39
|
+
res.fail('read_failed', 500);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return router;
|
|
44
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Discord admin command surface — ADR 0037, task #727.
|
|
4
|
+
//
|
|
5
|
+
// The ONLY command path by which a human triggers the bot to act on channels.
|
|
6
|
+
// Both routes are Archon-gated: requireBuilder resolves the live builder, then
|
|
7
|
+
// requireRank('archon') re-checks builders.rank server-side (ADR 0016). A
|
|
8
|
+
// Metic, Thetes, Xenos, or unauthenticated caller gets a 403 — this is the
|
|
9
|
+
// "lower ranks cannot command the bot" rule from task #727, enforced where it
|
|
10
|
+
// is load-bearing (the server) rather than in Discord roles.
|
|
11
|
+
//
|
|
12
|
+
// Passive bot features (ship-feed applause, #ideas inbound, role badges) are
|
|
13
|
+
// untouched by this file — they remain open to everyone by design.
|
|
14
|
+
|
|
15
|
+
const express = require('express');
|
|
16
|
+
const api = require('../../../src/module-api');
|
|
17
|
+
const auth = require('../auth');
|
|
18
|
+
const db = require('../db');
|
|
19
|
+
const shipBroadcast = require('../ship-broadcast');
|
|
20
|
+
const roleSync = require('../discord-role-sync');
|
|
21
|
+
|
|
22
|
+
// The module's kernel seams, registered when this factory runs (the loader runs
|
|
23
|
+
// it at boot, already gated to enabled instances — ADR 0083 / BV1.R47). They are
|
|
24
|
+
// how core reaches discord WITHOUT importing it:
|
|
25
|
+
// • PORT discord.isLinked(builderId) → bool — core (routes/me.js, builders.js)
|
|
26
|
+
// reads it via resolveOptional to render the hall's Connect/Unlink state and
|
|
27
|
+
// auto-clear onboarding's discord step; degrades to false when discord is off.
|
|
28
|
+
// • PORT discord.guildJoinSpec(rank) → { guildId, roleIds } — the OAuth link
|
|
29
|
+
// callback (core routes/auth.js) reads it to auto-join the just-linked builder
|
|
30
|
+
// to the guild with their rank role at add-time, and to pick the guild on
|
|
31
|
+
// unlink; degrades to { guildId: null } so the callback simply skips the join.
|
|
32
|
+
// • EVENT task.shipped — core (publish-reconciler / routes/tasks) emits it on the
|
|
33
|
+
// confirmed→shipped transition; we post the #ship-news / #ship-feed broadcast.
|
|
34
|
+
// • EVENT builder.role-sync-needed { builderId } — core emits it on a rank change
|
|
35
|
+
// (routes/builders.js) + after a link (routes/auth.js); we repair the Discord
|
|
36
|
+
// rank-role badge. Fire-and-forget; the listener owns its own error handling.
|
|
37
|
+
// All replace former direct core→discord requires. Idempotent: ports are
|
|
38
|
+
// hasProvider-guarded; listeners are replaced (off→on) so a test re-instantiating
|
|
39
|
+
// the router never accumulates duplicates.
|
|
40
|
+
let _shipListenerOff = null;
|
|
41
|
+
let _roleSyncListenerOff = null;
|
|
42
|
+
function registerSeams() {
|
|
43
|
+
if (!api.hasProvider('discord.isLinked')) {
|
|
44
|
+
api.registerProvider('discord.isLinked', async (builderId) => !!(await db.getDiscordLink(builderId)));
|
|
45
|
+
}
|
|
46
|
+
if (!api.hasProvider('discord.guildJoinSpec')) {
|
|
47
|
+
api.registerProvider('discord.guildJoinSpec', async (rank) => {
|
|
48
|
+
const guildId = roleSync.loadGuildId() || null;
|
|
49
|
+
const roleId = rank ? roleSync.rankToRoleId(rank, roleSync.loadRoleMap()) : null;
|
|
50
|
+
return { guildId, roleIds: roleId ? [roleId] : [] };
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (_shipListenerOff) _shipListenerOff();
|
|
54
|
+
_shipListenerOff = api.on('task.shipped', (task) => shipBroadcast.postShipBroadcast(task));
|
|
55
|
+
if (_roleSyncListenerOff) _roleSyncListenerOff();
|
|
56
|
+
_roleSyncListenerOff = api.on('builder.role-sync-needed', ({ builderId }) =>
|
|
57
|
+
roleSync.syncBuilderRole(builderId).catch((e) =>
|
|
58
|
+
console.log('[discord] role-sync (non-blocking):', e && e.message)));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = function buildDiscordRouter() {
|
|
62
|
+
registerSeams();
|
|
63
|
+
const router = express.Router();
|
|
64
|
+
|
|
65
|
+
// GET /api/bongos/discord/channels/plan — dry run. Returns the ops the bot WOULD
|
|
66
|
+
// apply to make Discord match docs/discord/channels.json, plus warnings. No
|
|
67
|
+
// writes. 503 when the bot is disabled / not connected to a guild.
|
|
68
|
+
// rank: archon — channel administration command.
|
|
69
|
+
router.get('/discord/channels/plan', auth.requireBuilder, auth.requireRank('archon'), async (req, res) => {
|
|
70
|
+
try {
|
|
71
|
+
const channels = require('../discord-channels');
|
|
72
|
+
const plan = channels.computePlan();
|
|
73
|
+
if (!plan.ok) {
|
|
74
|
+
return res.fail('bot_unavailable', 503, { reason: plan.reason });
|
|
75
|
+
}
|
|
76
|
+
return res.json({ ok: true, guild_id: plan.guildId, op_count: plan.ops.length, ops: plan.ops, warnings: plan.warnings });
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error('[gds] GET /discord/channels/plan', err);
|
|
79
|
+
return res.fail('internal_error', 500);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// POST /api/bongos/discord/channels/reconcile — apply the config to Discord now.
|
|
84
|
+
// Best-effort; returns how many ops applied/failed. 503 when the bot is
|
|
85
|
+
// disabled / not connected.
|
|
86
|
+
// rank: archon — channel administration command.
|
|
87
|
+
router.post('/discord/channels/reconcile', auth.requireBuilder, auth.requireRank('archon'), async (req, res) => {
|
|
88
|
+
try {
|
|
89
|
+
const channels = require('../discord-channels');
|
|
90
|
+
const result = await channels.reconcileChannels();
|
|
91
|
+
if (!result.ok) {
|
|
92
|
+
return res.fail('bot_unavailable', 503, { reason: result.reason });
|
|
93
|
+
}
|
|
94
|
+
return res.json({ ok: true, applied: result.applied, failed: result.failed, total: result.total, warnings: result.warnings });
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error('[gds] POST /discord/channels/reconcile', err);
|
|
97
|
+
return res.fail('internal_error', 500);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return router;
|
|
102
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Shared ship-broadcast routing + the server-side broadcast poster.
|
|
2
|
+
//
|
|
3
|
+
// chooseBroadcastTargets is the PURE channel-routing decision (task 990): given a
|
|
4
|
+
// shipped task's security flag, which Discord channels + lines the broadcast goes
|
|
5
|
+
// to. It lived in scripts/gds/ship.js; task 1045 extracted it here so BOTH the
|
|
6
|
+
// laptop-local ship.js path AND the server-mediated publish path (ADR 0055) route
|
|
7
|
+
// by the EXACT same rule — a security fix must never leak to a public channel from
|
|
8
|
+
// either path. ship.js re-exports it so its existing regression test is unchanged.
|
|
9
|
+
//
|
|
10
|
+
// postShipBroadcast is the SERVER-SIDE poster (task 1045). On the server-mediated
|
|
11
|
+
// publish path a dev box cannot post the broadcast — it holds no webhook secret
|
|
12
|
+
// (ADR 0053 "box holds nothing"), which is exactly why a box ship was silently
|
|
13
|
+
// skipping the public announcement. So when the GDS server confirms the deploy
|
|
14
|
+
// landed (publish-status reconcile, confirmed→shipped), the server — which holds
|
|
15
|
+
// the webhook secrets and just deployed — fires the broadcast instead. Best-effort:
|
|
16
|
+
// it never throws, so a Discord hiccup can never undo a ship.
|
|
17
|
+
|
|
18
|
+
// PURE. Returns the array of { channel, line } the caller posts, in order:
|
|
19
|
+
// - no summary → [] (the long-standing "no summary, no broadcast").
|
|
20
|
+
// - securitySensitive=true → ONLY the Archon-only #security-ship-feed. This
|
|
21
|
+
// branch can NEVER return ship_news or ship_feed — a security fix must not
|
|
22
|
+
// advertise the shape of an exploit on a public/builder channel before it is
|
|
23
|
+
// live (the owner requirement behind task 990).
|
|
24
|
+
// - otherwise → the builder #ship-feed only.
|
|
25
|
+
// #ship-feed / #security-ship-feed lines are PREFIXED with `#<taskId>` so the
|
|
26
|
+
// upvote bot can map a 👍 back to the task (discord-reaction.parseShipMarker).
|
|
27
|
+
//
|
|
28
|
+
// #ship-news is PAUSED (task 1749, owner ask 2026-07-03): per-ship broadcast was
|
|
29
|
+
// too spammy (it posted to #ship-news AND #ship-feed, ×2 posters = 4 messages).
|
|
30
|
+
// For now #ship-news gets nothing so exactly one #ship-feed message fires per
|
|
31
|
+
// ship. The broader "make the two channels genuinely different + filter by task
|
|
32
|
+
// type" work is idea 458 — un-pause by restoring the ship_news entry below.
|
|
33
|
+
function chooseBroadcastTargets({ securitySensitive, taskId, summary }) {
|
|
34
|
+
if (!summary) return [];
|
|
35
|
+
if (securitySensitive) {
|
|
36
|
+
return [{ channel: 'security_ship_feed', line: `#${taskId} shipped (security) — ${summary}` }];
|
|
37
|
+
}
|
|
38
|
+
return [
|
|
39
|
+
// { channel: 'ship_news', line: summary }, // PAUSED — task 1749 / idea 458
|
|
40
|
+
{ channel: 'ship_feed', line: `#${taskId} shipped — ${summary}` },
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Best-effort server-side broadcast for a task that just landed via server-
|
|
45
|
+
// mediated publish. `task` is the shipTask() row: { id, value_summary,
|
|
46
|
+
// security_sensitive, ... }. deps.postWebhook (the scripts/discord/lib poster) and
|
|
47
|
+
// deps.log are injectable for tests.
|
|
48
|
+
//
|
|
49
|
+
// FAIL CLOSED on the security flag, exactly like ship.js's local block: if the
|
|
50
|
+
// flag is not a definite boolean we SUPPRESS the broadcast entirely rather than
|
|
51
|
+
// risk advertising an unpatched exploit on a public channel. Returns
|
|
52
|
+
// { posted: [channels], skipped: reason|null } so the behavior is unit-testable;
|
|
53
|
+
// never throws (a single channel's failure is logged and swallowed).
|
|
54
|
+
async function postShipBroadcast(task, deps = {}) {
|
|
55
|
+
const result = { posted: [], skipped: null };
|
|
56
|
+
if (!task || task.id == null) { result.skipped = 'no_task'; return result; }
|
|
57
|
+
const summary = task.value_summary;
|
|
58
|
+
if (!summary) { result.skipped = 'no_summary'; return result; }
|
|
59
|
+
const sec = task.security_sensitive;
|
|
60
|
+
if (sec !== true && sec !== false) { result.skipped = 'security_flag_indeterminate'; return result; }
|
|
61
|
+
|
|
62
|
+
const postWebhook = deps.postWebhook || require('./webhook').postWebhook;
|
|
63
|
+
const log = deps.log || console.log;
|
|
64
|
+
const targets = chooseBroadcastTargets({ securitySensitive: sec, taskId: task.id, summary });
|
|
65
|
+
for (const { channel, line } of targets) {
|
|
66
|
+
try {
|
|
67
|
+
await postWebhook(channel, line);
|
|
68
|
+
result.posted.push(channel);
|
|
69
|
+
} catch (e) {
|
|
70
|
+
// A channel's failure (e.g. no webhook configured on this host) must never
|
|
71
|
+
// throw into the ship/reconcile path — log + continue (#94 / ADR 0032).
|
|
72
|
+
log(`[gds] ship broadcast: #${channel.replace(/_/g, '-')} post failed (non-blocking): ${e && e.message ? e.message : e}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { chooseBroadcastTargets, postShipBroadcast };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// modules/discord/webhook.js — the module's outbound Discord webhook poster
|
|
2
|
+
// (BV1.R47 / task 1416).
|
|
3
|
+
//
|
|
4
|
+
// A near-verbatim twin of scripts/discord/lib.js. Why a copy and not a shared
|
|
5
|
+
// require: the module boundary (ADR 0083) forbids a module reaching into core or
|
|
6
|
+
// scripts/, and the kernel doorway must not name a module — so the poster a
|
|
7
|
+
// module needs lives IN the module. scripts/discord/lib.js stays for the two CORE
|
|
8
|
+
// consumers that still post webhooks (src/bongos/approval-broadcast.js + the
|
|
9
|
+
// scripts/discord/post.js CLI). KEEP THE TWO IN SYNC — same channels, same
|
|
10
|
+
// 2000-char cap, same allowed_mentions suppression (#871). Config is read through
|
|
11
|
+
// the published doorway (configPath) so the path resolves identically.
|
|
12
|
+
//
|
|
13
|
+
// Discord is a one-way window into the GDS (the trust-boundary ADR): this only
|
|
14
|
+
// ever POSTs outward — it never reads authority or state back.
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const fs = require('node:fs');
|
|
19
|
+
const api = require('../../src/module-api');
|
|
20
|
+
|
|
21
|
+
// The configured instance config dir (OTB → ~/.config/otb, unchanged). R61.
|
|
22
|
+
const WEBHOOKS_PATH = api.configPath('discord-webhooks.json');
|
|
23
|
+
|
|
24
|
+
// Known channel keys. Each maps to a webhook URL in the file above, or to the
|
|
25
|
+
// env var DISCORD_<KEY>_WEBHOOK (upper-cased), which wins when set. Mirrors
|
|
26
|
+
// scripts/discord/lib.js exactly.
|
|
27
|
+
const CHANNELS = new Set([
|
|
28
|
+
'ship_news', 'ship_feed', 'security_ship_feed',
|
|
29
|
+
'member_approvals',
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
function loadWebhooks() {
|
|
33
|
+
let cfg = {};
|
|
34
|
+
try {
|
|
35
|
+
cfg = JSON.parse(fs.readFileSync(WEBHOOKS_PATH, 'utf8'));
|
|
36
|
+
} catch {
|
|
37
|
+
// File is optional when the URLs are provided via env vars instead.
|
|
38
|
+
}
|
|
39
|
+
for (const ch of CHANNELS) {
|
|
40
|
+
const envVal = process.env[`DISCORD_${ch.toUpperCase()}_WEBHOOK`];
|
|
41
|
+
if (envVal) cfg[ch] = envVal;
|
|
42
|
+
}
|
|
43
|
+
return cfg;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function postWebhook(channel, text) {
|
|
47
|
+
if (!CHANNELS.has(channel)) {
|
|
48
|
+
throw new Error(`Unknown Discord channel '${channel}'. Known: ${[...CHANNELS].join(', ')}`);
|
|
49
|
+
}
|
|
50
|
+
const url = loadWebhooks()[channel];
|
|
51
|
+
if (!url) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`No webhook URL for channel '${channel}'. Set it in ${WEBHOOKS_PATH} ` +
|
|
54
|
+
`or env DISCORD_${channel.toUpperCase()}_WEBHOOK. See scripts/discord/README.md.`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
// Discord hard-caps message content at 2000 chars.
|
|
58
|
+
const content = String(text).slice(0, 2000);
|
|
59
|
+
// SECURITY (#871): suppress ALL mention pings. The content is DB-sourced and
|
|
60
|
+
// attacker-influenceable; allowed_mentions:{parse:[]} renders text verbatim but
|
|
61
|
+
// fires zero notifications. (Discord defaults to parsing mentions when omitted.)
|
|
62
|
+
const res = await fetch(url, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
body: JSON.stringify({ content, allowed_mentions: { parse: [] } }),
|
|
66
|
+
});
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
const body = await res.text().catch(() => '');
|
|
69
|
+
throw new Error(`Discord webhook POST -> ${res.status}: ${body.slice(0, 300)}`);
|
|
70
|
+
}
|
|
71
|
+
return { ok: true, status: res.status }; // 204 No Content on success
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = { CHANNELS, WEBHOOKS_PATH, loadWebhooks, postWebhook };
|