@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,545 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Discord bot process โ Phase 3 F3 (ADR 0033 ยงF3).
|
|
4
|
+
//
|
|
5
|
+
// An in-process discord.js gateway client that runs INSIDE the existing Node
|
|
6
|
+
// server (no separate service โ one droplet, one process), started from
|
|
7
|
+
// server.js. This file is JUST the connection scaffolding: log in, stay
|
|
8
|
+
// connected, reconnect on transient drops, shut down cleanly. It performs NO
|
|
9
|
+
// GDS writes. Reactions (F4), role-sync (F5), and inbound (F6) build their
|
|
10
|
+
// handlers on top of the client this module owns โ but none of that is here.
|
|
11
|
+
//
|
|
12
|
+
// TRUST BOUNDARY (ADR 0033 ยง1 / ADR 0016): the bot is a service principal
|
|
13
|
+
// trusted ONLY to assert "this Discord user, who acted, is linked builder X."
|
|
14
|
+
// It is never an authority. Because F3 writes nothing, there is nothing here
|
|
15
|
+
// for the boundary to defend yet โ the invariant becomes load-bearing in F4.
|
|
16
|
+
//
|
|
17
|
+
// KILL-SWITCH (ADR 0033 "Hosting & secrets"): gated by DISCORD_BOT_ENABLED,
|
|
18
|
+
// default OFF, fail-closed โ exactly like BFG_WRITE_ENABLED (src/bongos/routes/
|
|
19
|
+
// memory.js). When off, start() is a no-op and discord.js is never even
|
|
20
|
+
// required, so a server with the switch off carries zero bot surface area.
|
|
21
|
+
//
|
|
22
|
+
// FAIL-SAFE: the web/game process is the priority. start() never throws and
|
|
23
|
+
// never crashes the server โ a missing token, a bad token, or a Discord
|
|
24
|
+
// outage logs and returns; the HTTP server keeps serving. The bot is
|
|
25
|
+
// best-effort infrastructure, not a critical dependency.
|
|
26
|
+
|
|
27
|
+
const auth = require('./auth');
|
|
28
|
+
const db = require('./db');
|
|
29
|
+
const ic = require('../../src/module-api');
|
|
30
|
+
|
|
31
|
+
let client = null; // the live discord.js Client (or null when disabled/down)
|
|
32
|
+
let starting = false; // guard against double-start
|
|
33
|
+
let reconcileTimer = null; // F5 periodic role-sync interval (cleared on stop)
|
|
34
|
+
let channelTimer = null; // ADR 0037 periodic channel-reconcile interval (cleared on stop)
|
|
35
|
+
|
|
36
|
+
// How often the bot re-syncs every linked builder's Discord rank-role from the
|
|
37
|
+
// live GDS rank (F5 reconcile, ADR 0033 ยง4). Catches anything the event-driven
|
|
38
|
+
// syncs missed (bot was down during a rank change, a manual role edit, etc.).
|
|
39
|
+
const ROLE_RECONCILE_MS = 6 * 60 * 60 * 1000; // 6h
|
|
40
|
+
|
|
41
|
+
// How often the bot re-applies the channel config (ADR 0037). Same cadence as
|
|
42
|
+
// role-sync; catches manual edits drifting the guild away from channels.json.
|
|
43
|
+
const CHANNEL_RECONCILE_MS = 6 * 60 * 60 * 1000; // 6h
|
|
44
|
+
|
|
45
|
+
// Kill-switch for the PASSIVE channel reconcile sweep (ADR 0037). Default OFF,
|
|
46
|
+
// fail-closed โ mirrors BFG_WRITE_ENABLED. When off, the bot never auto-edits
|
|
47
|
+
// channels; an Archon must trigger it explicitly via routes/discord.js.
|
|
48
|
+
function channelsManaged() {
|
|
49
|
+
return process.env.DISCORD_CHANNELS_MANAGED === 'true';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The ๐ reaction is the upvote signal (ADR 0033 ยง3). Match on the unicode
|
|
53
|
+
// emoji name discord.js reports for a standard thumbs-up.
|
|
54
|
+
const UPVOTE_EMOJI = '๐';
|
|
55
|
+
|
|
56
|
+
// The ๐/๐ approval reactions (task 1100). In #member-approvals a ๐ invites
|
|
57
|
+
// and a ๐ dismisses; mapped to a decision in discord-approvals.
|
|
58
|
+
const APPROVAL_EMOJI = new Set(['๐', '๐']);
|
|
59
|
+
|
|
60
|
+
// PURE โ the #ship-feed channel id the bot watches for reactions (F4). Env
|
|
61
|
+
// DISCORD_SHIP_FEED_CHANNEL_ID wins, else `ship_feed_channel_id` in
|
|
62
|
+
// ~/.config/otb/discord-bot.json. Returns null when unconfigured โ and the
|
|
63
|
+
// reaction handler then does NOTHING (fail-closed): with no channel pinned, the
|
|
64
|
+
// bot won't act on reactions in arbitrary channels.
|
|
65
|
+
function loadShipFeedChannelId() {
|
|
66
|
+
const envVal = process.env.DISCORD_SHIP_FEED_CHANNEL_ID;
|
|
67
|
+
if (envVal) return envVal;
|
|
68
|
+
try {
|
|
69
|
+
const fs = require('node:fs');
|
|
70
|
+
const path = require('node:path');
|
|
71
|
+
const os = require('node:os');
|
|
72
|
+
const raw = ic.readConfigFileSync('discord-bot.json');
|
|
73
|
+
const cfg = JSON.parse(raw);
|
|
74
|
+
if (cfg.ship_feed_channel_id) return String(cfg.ship_feed_channel_id);
|
|
75
|
+
} catch (_) { /* not configured */ }
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// PURE โ the #ideas channel id the bot files inbound messages from (F6). Env
|
|
80
|
+
// DISCORD_IDEAS_CHANNEL_ID wins, else `ideas_channel_id` in the creds file.
|
|
81
|
+
// Null โ inbound is inert (the handler files nothing).
|
|
82
|
+
function loadIdeasChannelId() {
|
|
83
|
+
const envVal = process.env.DISCORD_IDEAS_CHANNEL_ID;
|
|
84
|
+
if (envVal) return envVal;
|
|
85
|
+
try {
|
|
86
|
+
const fs = require('node:fs');
|
|
87
|
+
const path = require('node:path');
|
|
88
|
+
const os = require('node:os');
|
|
89
|
+
const raw = ic.readConfigFileSync('discord-bot.json');
|
|
90
|
+
const cfg = JSON.parse(raw);
|
|
91
|
+
if (cfg.ideas_channel_id) return String(cfg.ideas_channel_id);
|
|
92
|
+
} catch (_) { /* not configured */ }
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// PURE โ the #bugs channel id the bot files inbound bug-tasks from (F8). Env
|
|
97
|
+
// DISCORD_BUGS_CHANNEL_ID wins, else `bugs_channel_id` in the creds file.
|
|
98
|
+
// Null โ bug intake is inert (the handler files nothing). Mirrors #ideas.
|
|
99
|
+
function loadBugsChannelId() {
|
|
100
|
+
const envVal = process.env.DISCORD_BUGS_CHANNEL_ID;
|
|
101
|
+
if (envVal) return envVal;
|
|
102
|
+
try {
|
|
103
|
+
const fs = require('node:fs');
|
|
104
|
+
const path = require('node:path');
|
|
105
|
+
const os = require('node:os');
|
|
106
|
+
const raw = ic.readConfigFileSync('discord-bot.json');
|
|
107
|
+
const cfg = JSON.parse(raw);
|
|
108
|
+
if (cfg.bugs_channel_id) return String(cfg.bugs_channel_id);
|
|
109
|
+
} catch (_) { /* not configured */ }
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// PURE โ the #member-approvals channel id the bot watches for Archon ๐/๐ on
|
|
114
|
+
// new-builder access requests (task 1100). Env DISCORD_MEMBER_APPROVALS_CHANNEL_ID
|
|
115
|
+
// wins, else `member_approvals_channel_id` in the creds file. Null โ member
|
|
116
|
+
// approvals are inert (the handler acts on nothing โ fail-closed).
|
|
117
|
+
function loadMemberApprovalsChannelId() {
|
|
118
|
+
const envVal = process.env.DISCORD_MEMBER_APPROVALS_CHANNEL_ID;
|
|
119
|
+
if (envVal) return envVal;
|
|
120
|
+
try {
|
|
121
|
+
const fs = require('node:fs');
|
|
122
|
+
const path = require('node:path');
|
|
123
|
+
const os = require('node:os');
|
|
124
|
+
const raw = ic.readConfigFileSync('discord-bot.json');
|
|
125
|
+
const cfg = JSON.parse(raw);
|
|
126
|
+
if (cfg.member_approvals_channel_id) return String(cfg.member_approvals_channel_id);
|
|
127
|
+
} catch (_) { /* not configured */ }
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// PURE โ pull image attachment URLs off a discord.js message. An attachment is
|
|
132
|
+
// an image when it carries an image/* content-type (or, defensively, a width).
|
|
133
|
+
// Shared by the #bugs and #ideas handlers so both intakes treat images
|
|
134
|
+
// identically. Returns [] for a message with no image attachments.
|
|
135
|
+
function extractImageUrls(msg) {
|
|
136
|
+
const urls = [];
|
|
137
|
+
if (msg && msg.attachments && typeof msg.attachments.forEach === 'function') {
|
|
138
|
+
msg.attachments.forEach((a) => {
|
|
139
|
+
const isImage = (a.contentType && a.contentType.startsWith('image/')) || a.width != null;
|
|
140
|
+
if (isImage && a.url) urls.push(a.url);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return urls;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Build a thin #ideas / #bugs MessageCreate listener. The two inbound channels
|
|
147
|
+
// share this EXACT guard prologue โ fail-closed on channel config, ignore
|
|
148
|
+
// bots/webhooks, skip empty messages โ and differ only in the channel id, the
|
|
149
|
+
// aggregator, and the error-log label (task 1091, C7). NEVER throws into the
|
|
150
|
+
// gateway (the catch swallows + logs, same as the inlined copies).
|
|
151
|
+
function makeInboundChannelHandler({ channelId, aggregator, logLabel }) {
|
|
152
|
+
return async (msg) => {
|
|
153
|
+
try {
|
|
154
|
+
if (!channelId) return; // fail-closed: no channel pinned
|
|
155
|
+
if (msg.channelId !== channelId) return; // only the target channel
|
|
156
|
+
if (msg.author?.bot) return; // ignore bots/webhooks (incl. self)
|
|
157
|
+
if (msg.webhookId) return; // ignore webhook posts
|
|
158
|
+
const imageUrls = extractImageUrls(msg);
|
|
159
|
+
const hasText = msg.content && msg.content.trim();
|
|
160
|
+
if (!hasText && imageUrls.length === 0) return; // nothing to file
|
|
161
|
+
aggregator.enqueue({
|
|
162
|
+
discordUserId: msg.author.id,
|
|
163
|
+
content: msg.content || '',
|
|
164
|
+
imageUrls,
|
|
165
|
+
messageId: msg.id,
|
|
166
|
+
});
|
|
167
|
+
} catch (err) {
|
|
168
|
+
console.error(`[discord-bot] ${logLabel} error:`, err.message);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// PURE โ the kill-switch read. Mirrors BFG_WRITE_ENABLED: a flag is ON only
|
|
174
|
+
// when the env var is the exact string 'true'. Anything else (unset, '1',
|
|
175
|
+
// 'false', '') is OFF. Pure + injectable so it's unit-tested without env
|
|
176
|
+
// mutation. Default-OFF is the whole point: a fresh deploy never connects a
|
|
177
|
+
// bot until someone deliberately sets the flag.
|
|
178
|
+
function botEnabled(env = process.env) {
|
|
179
|
+
return env.DISCORD_BOT_ENABLED === 'true';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// The live client, for the feature modules that come later (F4/F5/F6). Null
|
|
183
|
+
// until start() has connected; consumers must null-check.
|
|
184
|
+
function getClient() {
|
|
185
|
+
return client;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Start the bot. Idempotent-ish: a no-op if disabled, already started, or
|
|
189
|
+
// already starting. Returns the Client on a login attempt, else null. Awaiting
|
|
190
|
+
// it resolves once login is *initiated* (discord.js fires 'ready' async after);
|
|
191
|
+
// it never rejects โ all failure is caught and logged so the caller (server.js
|
|
192
|
+
// boot) can't be taken down by Discord.
|
|
193
|
+
async function start() {
|
|
194
|
+
if (!botEnabled()) {
|
|
195
|
+
console.log('[discord-bot] disabled (DISCORD_BOT_ENABLED is not "true") โ not connecting.');
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
if (client || starting) {
|
|
199
|
+
console.log('[discord-bot] start() called but a client is already active โ skipping.');
|
|
200
|
+
return client;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const token = auth.loadDiscordBotToken();
|
|
204
|
+
if (!token) {
|
|
205
|
+
// Enabled but no token: a misconfiguration, not a crash. Log loudly and
|
|
206
|
+
// bail so the rest of the server still boots.
|
|
207
|
+
console.error('[discord-bot] DISCORD_BOT_ENABLED is on but no bot token is configured '
|
|
208
|
+
+ '(set DISCORD_BOT_TOKEN or bot_token in ~/.config/otb/discord-bot.json) โ not connecting.');
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
starting = true;
|
|
213
|
+
try {
|
|
214
|
+
// Lazy require: when the switch is off (the default), discord.js and its
|
|
215
|
+
// 240-package tree are never loaded into the process at all. Also keeps
|
|
216
|
+
// unit tests that don't enable the bot free of the dependency.
|
|
217
|
+
const { Client, GatewayIntentBits, Events, Partials } = require('discord.js');
|
|
218
|
+
const reaction = require('./discord-reaction');
|
|
219
|
+
const approvals = require('./discord-approvals');
|
|
220
|
+
const inbound = require('./discord-inbound');
|
|
221
|
+
const bugs = require('./discord-bugs');
|
|
222
|
+
|
|
223
|
+
// Intents (least-privilege, widened per feature): Guilds (F3) +
|
|
224
|
+
// GuildMessageReactions (F4 โ ๐ on #ship-feed) + GuildMembers (F5,
|
|
225
|
+
// PRIVILEGED โ fetch members to set rank roles) + GuildMessages +
|
|
226
|
+
// MessageContent (F6, PRIVILEGED โ read #ideas messages to file them as
|
|
227
|
+
// ideas). Partials let us receive reactions on messages NOT in the bot's
|
|
228
|
+
// cache (e.g. feed messages posted before the bot started).
|
|
229
|
+
const c = new Client({
|
|
230
|
+
intents: [
|
|
231
|
+
GatewayIntentBits.Guilds,
|
|
232
|
+
GatewayIntentBits.GuildMessageReactions,
|
|
233
|
+
GatewayIntentBits.GuildMembers,
|
|
234
|
+
GatewayIntentBits.GuildMessages,
|
|
235
|
+
GatewayIntentBits.MessageContent,
|
|
236
|
+
],
|
|
237
|
+
partials: [Partials.Message, Partials.Channel, Partials.Reaction],
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
c.once(Events.ClientReady, (ready) => {
|
|
241
|
+
const guilds = ready.guilds?.cache?.size ?? 0;
|
|
242
|
+
// ready.user.tag is the bot's name#id โ safe to log; the TOKEN never is.
|
|
243
|
+
console.log(`[discord-bot] connected as ${ready.user.tag} โ in ${guilds} guild(s).`);
|
|
244
|
+
if (guilds === 0) {
|
|
245
|
+
const worldName = (() => { try { return ic.branding().identity?.worldName || 'project'; } catch { return 'project'; } })();
|
|
246
|
+
console.log(`[discord-bot] note: bot is in 0 guilds. Invite it to the ${worldName} `
|
|
247
|
+
+ 'server (F5/role-sync needs it there with Manage Roles) before role/reaction features can act.');
|
|
248
|
+
}
|
|
249
|
+
// Ensure all managed rank roles exist in the guild โ creates any missing
|
|
250
|
+
// ones and saves their ids to the config file. Runs before the reconcile
|
|
251
|
+
// sweep so newly-created role ids are available for member syncs.
|
|
252
|
+
const roleSync = require('./discord-role-sync');
|
|
253
|
+
const guild = roleSync.loadGuildId()
|
|
254
|
+
? ready.guilds?.cache?.get(roleSync.loadGuildId()) || null
|
|
255
|
+
: ready.guilds?.cache?.first() || null;
|
|
256
|
+
roleSync.ensureRankRoles(guild).catch((e) =>
|
|
257
|
+
console.error('[discord-bot] ensureRankRoles failed:', e.message));
|
|
258
|
+
|
|
259
|
+
// F5 role-sync (ADR 0033 ยง4): an initial reconcile once connected, then a
|
|
260
|
+
// periodic sweep. Both best-effort โ a Discord perms/hierarchy problem
|
|
261
|
+
// logs inside the sync and never affects the GDS.
|
|
262
|
+
runRoleReconcile('on-connect');
|
|
263
|
+
reconcileTimer = setInterval(() => runRoleReconcile('periodic'), ROLE_RECONCILE_MS);
|
|
264
|
+
if (reconcileTimer.unref) reconcileTimer.unref(); // don't keep the process alive
|
|
265
|
+
|
|
266
|
+
// ADR 0037: passive channel reconcile โ make the guild's channel layout
|
|
267
|
+
// match docs/discord/channels.json. KILL-SWITCHED behind
|
|
268
|
+
// DISCORD_CHANNELS_MANAGED (default OFF, fail-closed) so the bot never
|
|
269
|
+
// surprise-edits a guild; when off, channels are only ever touched by an
|
|
270
|
+
// explicit Archon command (routes/discord.js). Best-effort, like role-sync.
|
|
271
|
+
if (channelsManaged()) {
|
|
272
|
+
runChannelReconcile('on-connect');
|
|
273
|
+
channelTimer = setInterval(() => runChannelReconcile('periodic'), CHANNEL_RECONCILE_MS);
|
|
274
|
+
if (channelTimer.unref) channelTimer.unref();
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// discord.js auto-reconnects transient gateway drops on its own; we only
|
|
279
|
+
// LOG the lifecycle so an operator can see the connection's health in the
|
|
280
|
+
// journal. No custom reconnect loop โ that would fight the library.
|
|
281
|
+
c.on(Events.Error, (err) => console.error('[discord-bot] client error:', err.message));
|
|
282
|
+
c.on(Events.Warn, (msg) => console.warn('[discord-bot] warning:', msg));
|
|
283
|
+
c.on(Events.ShardError, (err) => console.error('[discord-bot] shard error:', err.message));
|
|
284
|
+
c.on(Events.ShardDisconnect, (event, id) =>
|
|
285
|
+
console.warn(`[discord-bot] shard ${id} disconnected (code ${event?.code}) โ discord.js will attempt to reconnect.`));
|
|
286
|
+
c.on(Events.ShardReconnecting, (id) =>
|
|
287
|
+
console.log(`[discord-bot] shard ${id} reconnectingโฆ`));
|
|
288
|
+
c.on(Events.ShardResume, (id) =>
|
|
289
|
+
console.log(`[discord-bot] shard ${id} resumed.`));
|
|
290
|
+
// 'invalidated' = the session can't be recovered (e.g. token revoked).
|
|
291
|
+
// discord.js will NOT auto-reconnect; reconnecting would hammer the API.
|
|
292
|
+
// Log loudly, tear down, and leave it for a human โ a revoked token is an
|
|
293
|
+
// operator action, not a transient blip.
|
|
294
|
+
c.on(Events.Invalidated, () => {
|
|
295
|
+
console.error('[discord-bot] session invalidated (token revoked or auth failed) โ '
|
|
296
|
+
+ 'destroying client; a human must re-check the token and restart.');
|
|
297
|
+
stop().catch(() => {});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Resolve the watched #ship-feed channel id ONCE at startup โ NOT per
|
|
301
|
+
// reaction. loadShipFeedChannelId() can do a synchronous fs.readFileSync
|
|
302
|
+
// (file fallback); calling it inside the event handler would block the
|
|
303
|
+
// event loop on every ๐ guild-wide (F4 efficiency finding). Config is
|
|
304
|
+
// boot-time anyway (changing it needs a restart, like the kill-switch).
|
|
305
|
+
const shipFeedChannelId = loadShipFeedChannelId();
|
|
306
|
+
console.log(shipFeedChannelId
|
|
307
|
+
? `[discord-bot] watching #ship-feed (${shipFeedChannelId}) for ๐ upvotes.`
|
|
308
|
+
: '[discord-bot] no DISCORD_SHIP_FEED_CHANNEL_ID set โ upvoting is inert (no channel pinned).');
|
|
309
|
+
|
|
310
|
+
// Approval channel (task 1100): #member-approvals (Archon ๐/๐ admits a new
|
|
311
|
+
// builder). Resolved once at startup; null โ the approval surface is inert
|
|
312
|
+
// (fail-closed โ the bot won't act on reactions in arbitrary channels).
|
|
313
|
+
const memberApprovalsChannelId = loadMemberApprovalsChannelId();
|
|
314
|
+
console.log(memberApprovalsChannelId
|
|
315
|
+
? `[discord-bot] watching #member-approvals (${memberApprovalsChannelId}) for Archon ๐/๐.`
|
|
316
|
+
: '[discord-bot] no DISCORD_MEMBER_APPROVALS_CHANNEL_ID set โ member approvals are inert (no channel pinned).');
|
|
317
|
+
|
|
318
|
+
// F6 inbound: the #ideas channel whose messages become idea_inbox entries.
|
|
319
|
+
// Resolved once at startup (config is boot-time). Null โ inbound is inert.
|
|
320
|
+
const ideasChannelId = loadIdeasChannelId();
|
|
321
|
+
console.log(ideasChannelId
|
|
322
|
+
? `[discord-bot] watching #ideas (${ideasChannelId}) for inbound ideas.`
|
|
323
|
+
: '[discord-bot] no DISCORD_IDEAS_CHANNEL_ID set โ inbound idea filing is inert (no channel pinned).');
|
|
324
|
+
|
|
325
|
+
// #ideas auto-pairing + images (task #979): buffer a per-author burst of
|
|
326
|
+
// #ideas messages and file the combined burst (text + re-hosted images) as
|
|
327
|
+
// ONE idea once they go quiet. Mirrors the #bugs aggregator. Built once at
|
|
328
|
+
// startup; onFlush is the real filing path (recordInboundIdea).
|
|
329
|
+
const ideaAggregator = inbound.createIdeaAggregator({
|
|
330
|
+
onFlush: (combined) => inbound.recordInboundIdea(combined).then((result) => {
|
|
331
|
+
console.log(`[discord-bot] #ideas burst โ ${result.outcome}${result.ideaId ? ` (idea #${result.ideaId}, ${result.images || 0} img)` : (result.reason ? ` (${result.reason})` : '')}`);
|
|
332
|
+
return result;
|
|
333
|
+
}),
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// F8 bug intake (ADR 0047): the #bugs channel whose messages become GDS
|
|
337
|
+
// tasks (kind='bug') straight-through, under the anti-abuse guard in
|
|
338
|
+
// discord-bugs.js. Resolved once at startup. Null โ bug intake is inert.
|
|
339
|
+
const bugsChannelId = loadBugsChannelId();
|
|
340
|
+
console.log(bugsChannelId
|
|
341
|
+
? `[discord-bot] watching #bugs (${bugsChannelId}) for inbound bug-tasks.`
|
|
342
|
+
: '[discord-bot] no DISCORD_BUGS_CHANNEL_ID set โ inbound bug filing is inert (no channel pinned).');
|
|
343
|
+
|
|
344
|
+
// F8 auto-pairing (task #936): buffer a per-author burst of #bugs messages
|
|
345
|
+
// and file the combined burst as ONE bug once they go quiet. onFlush is the
|
|
346
|
+
// real filing path (recordInboundBug). Built once at startup.
|
|
347
|
+
const bugAggregator = bugs.createBugAggregator({
|
|
348
|
+
onFlush: (combined) => bugs.recordInboundBug(combined).then((result) => {
|
|
349
|
+
console.log(`[discord-bot] #bugs burst โ ${result.outcome}${result.taskId ? ` (task #${result.taskId}, ${result.status}, ${result.images || 0} img)` : (result.reason ? ` (${result.reason})` : '')}`);
|
|
350
|
+
return result;
|
|
351
|
+
}),
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Upvoting (F4, ADR 0033 ยง3). A ๐ in #ship-feed โ real peer-vote (if the
|
|
355
|
+
// reacting user is linked to a Metic+ non-author) or cosmetic applause
|
|
356
|
+
// (everyone else). ALL authorization is re-derived server-side in
|
|
357
|
+
// discord-reaction.recordDiscordReaction against the linked builder's LIVE
|
|
358
|
+
// rank โ the bot only asserts WHO reacted, never WHAT they may do. This
|
|
359
|
+
// handler is deliberately thin and NEVER throws into the gateway. Cheap
|
|
360
|
+
// gates (captured channel id, emoji, channel match) run BEFORE any network
|
|
361
|
+
// fetch so off-target reactions cost nothing.
|
|
362
|
+
c.on(Events.MessageReactionAdd, async (rxn, user) => {
|
|
363
|
+
try {
|
|
364
|
+
if (user?.bot) return; // ignore bots, incl. self
|
|
365
|
+
if (!shipFeedChannelId) return; // fail-closed: no channel pinned
|
|
366
|
+
if (rxn.emoji?.name !== UPVOTE_EMOJI) return; // only ๐ is the upvote
|
|
367
|
+
// channelId is present on the partial reaction (no fetch needed yet) โ
|
|
368
|
+
// reject off-channel reactions before paying for any hydration.
|
|
369
|
+
if (rxn.message?.channelId && rxn.message.channelId !== shipFeedChannelId) return;
|
|
370
|
+
// Reactions on uncached messages arrive partial โ hydrate before reading.
|
|
371
|
+
if (rxn.partial) { try { await rxn.fetch(); } catch { return; } }
|
|
372
|
+
const msg = rxn.message;
|
|
373
|
+
if (msg.partial) { try { await msg.fetch(); } catch { return; } }
|
|
374
|
+
if (msg.channelId !== shipFeedChannelId) return; // only #ship-feed (re-check post-hydrate)
|
|
375
|
+
// Only count reactions on WEBHOOK-posted ledger lines (Mozzie). A
|
|
376
|
+
// webhook message carries a non-null webhookId; a normal member message
|
|
377
|
+
// does not, and a member cannot post as a webhook (needs Manage
|
|
378
|
+
// Webhooks + the secret URL). This makes the F4 Hacker "fake #<id> line"
|
|
379
|
+
// concern fail-closed IN CODE, not just via the channel-permission lock.
|
|
380
|
+
if (!msg.webhookId) return;
|
|
381
|
+
const taskId = reaction.parseShipMarker(msg.content || '');
|
|
382
|
+
if (!taskId) return; // not a ship-marker line
|
|
383
|
+
const result = await reaction.recordDiscordReaction({ discordUserId: user.id, taskId });
|
|
384
|
+
// Don't log the reacting user's Discord id on the success path โ the
|
|
385
|
+
// systemd journal is less access-controlled than audit_log (Archon-only),
|
|
386
|
+
// which already records the id with surface='discord_bot'. Log only the
|
|
387
|
+
// task + outcome here (Hacker finding).
|
|
388
|
+
console.log(`[discord-bot] ๐ on task #${taskId} โ ${result.outcome}`);
|
|
389
|
+
} catch (err) {
|
|
390
|
+
console.error('[discord-bot] reaction handler error:', err.message);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// Archon approvals (task 1100). A ๐/๐ in #member-approvals, by an Archon, on a
|
|
395
|
+
// webhook-posted request line โ the existing approve/decline action (admit a
|
|
396
|
+
// new builder). ALL authorization is re-derived server-side in
|
|
397
|
+
// discord-approvals against the reacting builder's LIVE rank (must be Archon) โ
|
|
398
|
+
// the bot only asserts WHO reacted. Thin, fail-closed on channel config, NEVER
|
|
399
|
+
// throws into the gateway. Cheap gates (channel pinned, emoji, channel match)
|
|
400
|
+
// run before any fetch.
|
|
401
|
+
c.on(Events.MessageReactionAdd, async (rxn, user) => {
|
|
402
|
+
try {
|
|
403
|
+
if (user?.bot) return; // ignore bots, incl. self
|
|
404
|
+
if (!memberApprovalsChannelId) return; // fail-closed
|
|
405
|
+
if (!APPROVAL_EMOJI.has(rxn.emoji?.name)) return; // only ๐ / ๐
|
|
406
|
+
const ch = rxn.message?.channelId;
|
|
407
|
+
// Pre-hydration channel gate: reject reactions outside the approval
|
|
408
|
+
// channel before paying for any fetch.
|
|
409
|
+
if (ch && ch !== memberApprovalsChannelId) return;
|
|
410
|
+
if (rxn.partial) { try { await rxn.fetch(); } catch { return; } }
|
|
411
|
+
const msg = rxn.message;
|
|
412
|
+
if (msg.partial) { try { await msg.fetch(); } catch { return; } }
|
|
413
|
+
// Confirm post-hydration that this is the #member-approvals channel.
|
|
414
|
+
if (msg.channelId !== memberApprovalsChannelId) return;
|
|
415
|
+
const kind = 'member';
|
|
416
|
+
// Only act on WEBHOOK-posted request lines (the GDS posts these). A member
|
|
417
|
+
// message carries no webhookId and cannot forge one โ so a hand-typed
|
|
418
|
+
// "member-request #5" can't trigger an approval. Fail-closed in code.
|
|
419
|
+
if (!msg.webhookId) return;
|
|
420
|
+
const marker = approvals.parseApprovalMarker(msg.content || '');
|
|
421
|
+
if (!marker || marker.kind !== kind) return; // not a request line / wrong channel
|
|
422
|
+
const decision = approvals.emojiToDecision(rxn.emoji.name);
|
|
423
|
+
if (!decision) return;
|
|
424
|
+
const result = await approvals.recordApprovalReaction({
|
|
425
|
+
discordUserId: user.id, kind, decision, recordId: marker.id,
|
|
426
|
+
});
|
|
427
|
+
// Post a short confirmation back to the channel for the ones that acted,
|
|
428
|
+
// so the Archon sees the outcome inline. Best-effort via the webhook (the
|
|
429
|
+
// bot posts nothing of authority); silent on no-ops / ignores.
|
|
430
|
+
if (result.outcome === 'approved' || result.outcome === 'declined') {
|
|
431
|
+
const verb = decision === 'approve' ? 'invited to the hall' : 'dismissed';
|
|
432
|
+
const who = result.resolved && result.resolved.github_login
|
|
433
|
+
? result.resolved.github_login
|
|
434
|
+
: `member-request #${marker.id}`;
|
|
435
|
+
require('./webhook')
|
|
436
|
+
.postWebhook('member_approvals', `โ
${who} ${verb}.`)
|
|
437
|
+
.catch(() => {});
|
|
438
|
+
}
|
|
439
|
+
// Don't log the reacting Discord id (audit_log already records it,
|
|
440
|
+
// Archon-only). Log only the record + outcome.
|
|
441
|
+
console.log(`[discord-bot] approval ${kind} #${marker.id} (${decision}) โ ${result.outcome}`);
|
|
442
|
+
} catch (err) {
|
|
443
|
+
console.error('[discord-bot] approval handler error:', err.message);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// Inbound idea filing (F6, ADR 0033 ยง5). A message in #ideas โ an
|
|
448
|
+
// idea_inbox entry (attributed to the linked builder when known). All
|
|
449
|
+
// filing logic + attribution + audit lives in discord-inbound; this handler
|
|
450
|
+
// is thin, fail-closed on channel config, and NEVER throws into the gateway.
|
|
451
|
+
c.on(Events.MessageCreate, makeInboundChannelHandler({
|
|
452
|
+
channelId: ideasChannelId, aggregator: ideaAggregator, logLabel: 'inbound handler',
|
|
453
|
+
}));
|
|
454
|
+
|
|
455
|
+
// Inbound bug filing (F8, ADR 0047). A message in #bugs โ a GDS task
|
|
456
|
+
// (kind='bug') straight-through, under the anti-abuse guard (linked-only +
|
|
457
|
+
// rank-aware landing + per-builder rate-limit + body screen) โ all in
|
|
458
|
+
// discord-bugs.recordInboundBug. This handler is thin, fail-closed on
|
|
459
|
+
// channel config, and NEVER throws into the gateway. Separate listener from
|
|
460
|
+
// #ideas so the two intakes stay structurally independent.
|
|
461
|
+
c.on(Events.MessageCreate, makeInboundChannelHandler({
|
|
462
|
+
channelId: bugsChannelId, aggregator: bugAggregator, logLabel: 'bug handler',
|
|
463
|
+
}));
|
|
464
|
+
|
|
465
|
+
// Role-on-arrival (F7, ADR 0033 โ the link-first onboarding gate's safety
|
|
466
|
+
// net). The auto-join path (routes/auth.js) already sets the rank role at
|
|
467
|
+
// add-time, so a builder who linked-then-joined is correct immediately. This
|
|
468
|
+
// handler covers every OTHER way a member can appear โ a stray pre-existing
|
|
469
|
+
// invite, a manual add, a re-join after leaving โ by re-deriving their role
|
|
470
|
+
// from their LIVE GDS rank the instant they arrive. ONE-WAY (ADR 0016): it
|
|
471
|
+
// reads builders.rank and writes a Discord role, never the reverse. An
|
|
472
|
+
// unlinked arrival maps to no builder and is left untouched. Thin, never
|
|
473
|
+
// throws into the gateway.
|
|
474
|
+
c.on(Events.GuildMemberAdd, async (member) => {
|
|
475
|
+
try {
|
|
476
|
+
if (member?.user?.bot) return; // ignore bots, incl. self
|
|
477
|
+
const builder = await db.getBuilderByDiscordUserId(member.id);
|
|
478
|
+
if (!builder) return; // unlinked arrival โ nothing to sync
|
|
479
|
+
const roleSync = require('./discord-role-sync');
|
|
480
|
+
const r = await roleSync.syncBuilderRole(builder.id);
|
|
481
|
+
console.log(`[discord-bot] member joined โ role-sync for builder ${builder.id}: ${r.ok ? (r.noop ? 'noop' : 'synced') : r.reason}`);
|
|
482
|
+
} catch (err) {
|
|
483
|
+
console.error('[discord-bot] member-add handler error:', err.message);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
client = c;
|
|
488
|
+
// login() rejects on a bad token / network failure โ catch it so a Discord
|
|
489
|
+
// problem never propagates into the HTTP boot path.
|
|
490
|
+
await c.login(token).catch((err) => {
|
|
491
|
+
console.error('[discord-bot] login failed:', err.message);
|
|
492
|
+
// Tear the half-built client down so a later start() can retry cleanly.
|
|
493
|
+
try { c.destroy(); } catch (_) { /* ignore */ }
|
|
494
|
+
client = null;
|
|
495
|
+
});
|
|
496
|
+
return client;
|
|
497
|
+
} catch (err) {
|
|
498
|
+
console.error('[discord-bot] failed to start:', err.message);
|
|
499
|
+
client = null;
|
|
500
|
+
return null;
|
|
501
|
+
} finally {
|
|
502
|
+
starting = false;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Run the F5 role-sync reconcile sweep (best-effort, lazy-required to avoid a
|
|
507
|
+
// require cycle with discord-role-sync). Logs a one-line summary.
|
|
508
|
+
function runRoleReconcile(trigger) {
|
|
509
|
+
const roleSync = require('./discord-role-sync');
|
|
510
|
+
roleSync.reconcileAll()
|
|
511
|
+
.then((r) => console.log(`[discord-bot] role-sync reconcile (${trigger}): ${r.synced}/${r.total} linked builders synced.`))
|
|
512
|
+
.catch((err) => console.error(`[discord-bot] role-sync reconcile (${trigger}) failed:`, err.message));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Run the ADR 0037 channel reconcile sweep (best-effort, lazy-required). Makes
|
|
516
|
+
// the guild match docs/discord/channels.json. Logs a one-line summary.
|
|
517
|
+
function runChannelReconcile(trigger) {
|
|
518
|
+
const channels = require('./discord-channels');
|
|
519
|
+
channels.reconcileChannels()
|
|
520
|
+
.then((r) => {
|
|
521
|
+
if (!r.ok) console.log(`[discord-bot] channel reconcile (${trigger}): skipped (${r.reason}).`);
|
|
522
|
+
else console.log(`[discord-bot] channel reconcile (${trigger}): ${r.applied} applied, ${r.failed} failed of ${r.total}.`);
|
|
523
|
+
})
|
|
524
|
+
.catch((err) => console.error(`[discord-bot] channel reconcile (${trigger}) failed:`, err.message));
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Graceful shutdown โ close the gateway connection. Safe to call when never
|
|
528
|
+
// started (no-op). Idempotent. Wired to SIGTERM/SIGINT in server.js so a
|
|
529
|
+
// systemd restart / Ctrl-C closes the socket cleanly instead of leaving a
|
|
530
|
+
// zombie session that Discord later reaps.
|
|
531
|
+
async function stop() {
|
|
532
|
+
if (reconcileTimer) { clearInterval(reconcileTimer); reconcileTimer = null; }
|
|
533
|
+
if (channelTimer) { clearInterval(channelTimer); channelTimer = null; }
|
|
534
|
+
if (!client) return;
|
|
535
|
+
const c = client;
|
|
536
|
+
client = null;
|
|
537
|
+
try {
|
|
538
|
+
await c.destroy();
|
|
539
|
+
console.log('[discord-bot] disconnected.');
|
|
540
|
+
} catch (err) {
|
|
541
|
+
console.error('[discord-bot] error during shutdown:', err.message);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
module.exports = { start, stop, getClient, botEnabled, loadShipFeedChannelId, loadIdeasChannelId, loadBugsChannelId, loadMemberApprovalsChannelId, channelsManaged };
|