@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,50 @@
|
|
|
1
|
+
# `discord` module โ the Discord mirror (bot + broadcast + link/role)
|
|
2
|
+
|
|
3
|
+
Optional feature module: the outbound `#ship-news`/`#ship-feed` broadcast, the inbound `#ideas`/`#bugs` bot, ship-feed applause, role-sync, channel management, and the account-link state the hall reads. **Discord is a one-way mirror of the GDS โ never authority** ([ADR 0033](../../docs/adr/<redacted>.md)). Gated by `config/modules.json` (`discord: true` for the OTB instance; off on a vanilla Cloud Bongos boot). The loader (`src/module-loader/loader.js`) discovers this dir via `module.json`, mounts the route factories, and starts the bot poller. Extracted from core in BV1.R47 ([task 1416](https://example.com/builders#/task/1416)).
|
|
4
|
+
|
|
5
|
+
## The boundary (the one rule)
|
|
6
|
+
|
|
7
|
+
This module reaches core **only** through the published doorway `../../src/module-api.js` (the semver'd contract; this module needs `coreVersion ^1.4.0`). It must **never** `require()` a core internal (`../../src/bongos/db`, `../../src/bongos/auth`, `../../scripts/discord/lib`, โฆ) directly, and never another module. Core, in turn, must never `require()` a file in here โ that's the forbidden coreโmodule edge this extraction removed (enforced by the BV1.R44/R45 fitness checks).
|
|
8
|
+
|
|
9
|
+
## Files
|
|
10
|
+
|
|
11
|
+
| File | What |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `routes/discord.js` | Archon-only `/discord/channels/{plan,reconcile}` (ADR 0037). Its factory also **registers this module's seams** (port + listener; idempotent). |
|
|
14
|
+
| `routes/bug-attachments.js` | serves `GET /bug-attachments/:name` (images attached to `#bugs` reports). Moved with the lib it reads. |
|
|
15
|
+
| `discord-bot.js` | the gateway client (reactions, inbound, role-sync timer). Started/stopped via `pollers/discord-bot.js`. |
|
|
16
|
+
| `discord-reaction.js` ยท `discord-inbound.js` ยท `discord-bugs.js` ยท `discord-approvals.js` | the passive bot features: ๐ applause, `#ideas` โ idea_inbox, `#bugs` โ task, member-approval reactions. |
|
|
17
|
+
| `discord-role-sync.js` ยท `discord-channels.js` | rankโrole badge reconcile + channel/topic/view management from `docs/discord/channels.json`. |
|
|
18
|
+
| `discord-message-buffer.js` ยท `discord-audit.js` ยท `bug-attachments.js` | message-pair aggregator, bot-action audit helper, attachment persistence (pure). |
|
|
19
|
+
| `ship-broadcast.js` | the server-side ship poster โ fired by the `task.shipped` seam listener, not a core import. |
|
|
20
|
+
| `db.js` | the module's data layer โ owns the `discord_links`/`discord_applause` queries (via the doorway `pool`) + re-exports kernel db helpers. |
|
|
21
|
+
| `auth.js` ยท `webhook.js` | **vendored twins** of core helpers (see below). |
|
|
22
|
+
| `pollers/discord-bot.js` | the loader's web-process poller entry โ `start()`/`stop()` over the bot. |
|
|
23
|
+
|
|
24
|
+
## Seams (how core and this module talk without importing each other)
|
|
25
|
+
|
|
26
|
+
- **Provides โ port `discord.isLinked`** `(builderId) โ Promise<bool>`. Core (`routes/me.js`, `routes/builders.js`) reads it via `resolveOptional('discord.isLinked', async () => false)` so the hall renders Connect/Unlink and onboarding's discord step auto-clears; degrades to `false` on a discord-less instance. (`discord.configured` โ does the server hold OAuth creds โ stays **core**: the account-LINK flow in `routes/auth.js` is not part of this module.)
|
|
27
|
+
- **Listens โ event `task.shipped`** `(shippedTaskRow)`. Core (`publish-reconciler.js`, `routes/tasks.js`) emits it on the confirmedโshipped transition; this module posts the `#ship-news`/`#ship-feed` broadcast. Replaces the old `require('./ship-broadcast')` coreโdiscord import.
|
|
28
|
+
- No consumed ports.
|
|
29
|
+
|
|
30
|
+
Registration happens in `routes/discord.js`'s factory (the loader runs it at boot, already module-gated): the port is `hasProvider`-guarded; the `task.shipped` listener is offโon replaced so a test re-instantiating the router never accumulates duplicates.
|
|
31
|
+
|
|
32
|
+
## Vendored twins (boundary-forced โ keep in sync)
|
|
33
|
+
|
|
34
|
+
The kernel doorway must stay module-agnostic (the "core carries no module code" fitness check forbids naming a module in `src/module-api.js`), and a module can't require core/scripts. So three small helpers are **copied** here, with their core originals kept for the parts of Discord that stay core:
|
|
35
|
+
|
|
36
|
+
- **`auth.js`** โ `isValidDiscordUserId` + `loadDiscordBotToken`. Core `src/bongos/auth.js` keeps its copies for the OAuth account-LINK flow (`routes/auth.js`).
|
|
37
|
+
- **`webhook.js`** โ `postWebhook`/`loadWebhooks`/`CHANNELS`, a twin of `scripts/discord/lib.js` (which stays for core `approval-broadcast.js` + the `scripts/discord/post.js` CLI).
|
|
38
|
+
- **`db.js`** โ `getDiscordLink`/`recordApplause` are also defined in core `src/bongos/db.js` (still used by the link flow / a seed); the others (`getBuilderByDiscordUserId`, `listDiscordLinks`, `countRecentDiscordBugFilings`) are owned here.
|
|
39
|
+
|
|
40
|
+
## NOT in this module
|
|
41
|
+
|
|
42
|
+
- **The OAuth account-link flow** โ `routes/auth.js` + the Discord OAuth helpers in `src/bongos/auth.js` (`discordConfigured`, `addDiscordGuildMember`, โฆ). Linking writes a core `builders`-adjacent row; it stays core. This module only surfaces the resulting *state* via the `discord.isLinked` port.
|
|
43
|
+
- **`migrations/<redacted>.sql`** โ stays in **core** `migrations/`. It also `ALTER`s core tables (`builders.system_role`, `audit_log.surface`) and seeds the `discord_bot` principal, so the module-migration namespacing rule (which forbids a module migration touching non-`discord_` objects) can't host it. Any *future* discord migration that touches only `discord_*` objects can live in `modules/discord/migrations/`.
|
|
44
|
+
- **`ship-card.js`** โ the CLI lifecycle card (claimedโshipped) used by `claim.js`/`ship.js` on every instance; not a Discord concern, stays in `src/bongos/`.
|
|
45
|
+
- **`approval-broadcast.js` ยท `scripts/discord/{lib,post}.js`** โ core/CLI Discord posters (see Vendored twins).
|
|
46
|
+
- **The hall UI** (`modules/hall-ui/public/`, carved in BV1.R85) โ per-module client UI is a later design phase; it stays gated by `window.__MODULES__.enabled['discord']`.
|
|
47
|
+
|
|
48
|
+
## Tests
|
|
49
|
+
|
|
50
|
+
`tests/discord_*.mjs` + `tests/bug_attachments.mjs` import from `modules/discord/โฆ`. `scripts/discord/smoke*.sh` (if present) exercises the outbound poster.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// modules/discord/auth.js โ the discord module's auth surface (BV1.R47 / task 1416).
|
|
2
|
+
//
|
|
3
|
+
// One `auth` namespace behind which the moved discord-*.js files keep their
|
|
4
|
+
// `const auth = require('./auth')` call sites unchanged:
|
|
5
|
+
//
|
|
6
|
+
// โข KERNEL gates, re-exported off the published doorway โ requireBuilder /
|
|
7
|
+
// requireRank (the same rank machinery every route uses; ADR 0016).
|
|
8
|
+
//
|
|
9
|
+
// โข DISCORD-SPECIFIC helpers VENDORED here โ the snowflake validator + the bot-
|
|
10
|
+
// token loader. These are deliberately NOT on the kernel doorway: the doorway
|
|
11
|
+
// stays module-agnostic (the "core carries no module code" fitness check
|
|
12
|
+
// forbids naming a module in src/module-api.js). Core's src/bongos/auth.js keeps
|
|
13
|
+
// its OWN copies for the OAuth account-LINK flow (routes/auth.js, which is not
|
|
14
|
+
// part of this module); these are byte-identical twins โ keep them in sync.
|
|
15
|
+
//
|
|
16
|
+
// The boundary: this file requires ONLY src/module-api.js.
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const api = require('../../src/module-api');
|
|
21
|
+
|
|
22
|
+
// PURE โ a Discord user id is a numeric snowflake (17โ20 digits). Reject anything
|
|
23
|
+
// else BEFORE it reaches a link/insert. (Twin of src/bongos/auth.js's copy, kept for
|
|
24
|
+
// the link flow; this module no longer reaches into core auth.)
|
|
25
|
+
function isValidDiscordUserId(id) {
|
|
26
|
+
return typeof id === 'string' && /^\d{17,20}$/.test(id);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Bot-token loader (ADR 0033 ยงF3). The bot authenticates to the Discord GATEWAY
|
|
30
|
+
// with a bot token. env DISCORD_BOT_TOKEN wins (the systemd path ADR 0022 prefers
|
|
31
|
+
// for the always-on droplet), else the bot_token field of the local
|
|
32
|
+
// discord-bot.json (mode 600). The token is a secret โ never log it. Reads config
|
|
33
|
+
// through the doorway's instance-config accessor. (Twin of src/bongos/auth.js's copy.)
|
|
34
|
+
function loadDiscordBotToken() {
|
|
35
|
+
const envToken = process.env.DISCORD_BOT_TOKEN;
|
|
36
|
+
if (envToken) return envToken;
|
|
37
|
+
try {
|
|
38
|
+
const raw = api.readConfigFileSync('discord-bot.json');
|
|
39
|
+
if (raw) {
|
|
40
|
+
const creds = JSON.parse(raw);
|
|
41
|
+
if (creds.bot_token) return creds.bot_token;
|
|
42
|
+
}
|
|
43
|
+
} catch (_) { /* not configured */ }
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
// kernel gates (doorway)
|
|
49
|
+
requireBuilder: api.requireBuilder,
|
|
50
|
+
requireRank: api.requireRank,
|
|
51
|
+
// vendored discord-specific helpers
|
|
52
|
+
isValidDiscordUserId,
|
|
53
|
+
loadDiscordBotToken,
|
|
54
|
+
};
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Bug-attachment storage โ durable re-hosting of #bugs screenshots (task #936,
|
|
4
|
+
// follow-on to ADR 0047 / task #931).
|
|
5
|
+
//
|
|
6
|
+
// Discord CDN attachment URLs are signed and EXPIRE (~24h). A bug-task can sit
|
|
7
|
+
// in the queue for days before it's claimed, so a bare Discord link would be a
|
|
8
|
+
// dead image by then. Lars's call (2026-06-12): SAVE OUR OWN COPY. The bot
|
|
9
|
+
// downloads each image attachment and re-hosts it on our server under a
|
|
10
|
+
// builders-only, auth-gated route (routes/bug-attachments.js), and the task
|
|
11
|
+
// description embeds that durable link.
|
|
12
|
+
//
|
|
13
|
+
// This module is the pure-ish storage core: where files live, how a filename
|
|
14
|
+
// is validated (path-traversal-safe), and how one image is fetched + screened
|
|
15
|
+
// + written. It has NO express/discord deps so it unit-tests with an injected
|
|
16
|
+
// fetch.
|
|
17
|
+
//
|
|
18
|
+
// ANTI-ABUSE (extends the ADR 0047 guard to bytes): only image/* with an
|
|
19
|
+
// allowlisted extension, magic-byte sniff (don't trust the content-type
|
|
20
|
+
// header), a per-image size cap, a per-bug image cap, and a strict
|
|
21
|
+
// download-host allowlist (the URL must be a Discord CDN host โ defense against
|
|
22
|
+
// a crafted attachment pointing our fetch at something else, i.e. SSRF).
|
|
23
|
+
|
|
24
|
+
const path = require('node:path');
|
|
25
|
+
const fs = require('node:fs/promises');
|
|
26
|
+
const crypto = require('node:crypto');
|
|
27
|
+
|
|
28
|
+
// ext โ content-type for the formats we accept.
|
|
29
|
+
const EXT_BY_TYPE = {
|
|
30
|
+
'image/png': 'png',
|
|
31
|
+
'image/jpeg': 'jpg',
|
|
32
|
+
'image/gif': 'gif',
|
|
33
|
+
'image/webp': 'webp',
|
|
34
|
+
};
|
|
35
|
+
const TYPE_BY_EXT = {
|
|
36
|
+
png: 'image/png',
|
|
37
|
+
jpg: 'image/jpeg',
|
|
38
|
+
gif: 'image/gif',
|
|
39
|
+
webp: 'image/webp',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const MAX_BYTES = 8 * 1024 * 1024; // 8 MB per image
|
|
43
|
+
const MAX_IMAGES_PER_BUG = 6; // cap attachments folded into one bug
|
|
44
|
+
|
|
45
|
+
// Stored filenames are bot-generated, never user-supplied: a fixed shape we can
|
|
46
|
+
// validate exactly. This is the whole path-traversal defense โ the serve route
|
|
47
|
+
// only ever opens a name that matches this.
|
|
48
|
+
const NAME_RE = /^bug-[a-z0-9]{1,40}-[0-9a-f]{16}\.(png|jpg|gif|webp)$/;
|
|
49
|
+
|
|
50
|
+
// Where re-hosted images live. Outside the git tree (a `var/` dir) so a deploy's
|
|
51
|
+
// `git reset --hard` / pull never wipes them; gitignored. Env override for tests
|
|
52
|
+
// / non-default layouts.
|
|
53
|
+
function attachmentsDir() {
|
|
54
|
+
return process.env.BUG_ATTACHMENTS_DIR || path.join(__dirname, '..', '..', 'var', 'bug-attachments');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// PURE โ is this a well-formed, bot-generated attachment name?
|
|
58
|
+
function isSafeName(name) {
|
|
59
|
+
return typeof name === 'string' && NAME_RE.test(name);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// PURE โ resolve a name to an absolute path INSIDE attachmentsDir, or null if
|
|
63
|
+
// the name is unsafe or would escape the dir. Belt (regex) and braces (prefix
|
|
64
|
+
// check on the resolved path).
|
|
65
|
+
function resolveSafe(name) {
|
|
66
|
+
if (!isSafeName(name)) return null;
|
|
67
|
+
const dir = path.resolve(attachmentsDir());
|
|
68
|
+
const p = path.resolve(dir, name);
|
|
69
|
+
if (p !== path.join(dir, name)) return null;
|
|
70
|
+
if (!p.startsWith(dir + path.sep)) return null;
|
|
71
|
+
return p;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// PURE โ the content-type to serve a stored file with (from its extension).
|
|
75
|
+
function contentTypeForName(name) {
|
|
76
|
+
const ext = String(name).split('.').pop().toLowerCase();
|
|
77
|
+
return TYPE_BY_EXT[ext] || 'application/octet-stream';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// PURE โ only fetch from Discord's own CDN hosts. Anything else is refused
|
|
81
|
+
// before a network call (SSRF guard). Discord serves message attachments from
|
|
82
|
+
// EXACTLY these two hosts, so we match them exactly rather than by a
|
|
83
|
+
// `.discordapp.{com,net}` suffix. (L4 residual, red-team 2026-06-12: the suffix
|
|
84
|
+
// form would also accept any future/attacker-influenced `*.discordapp.com` or
|
|
85
|
+
// `*.discordapp.net` subdomain โ a value-judgement we don't need to make for an
|
|
86
|
+
// attachment fetch where the real hosts are known and fixed.)
|
|
87
|
+
const ALLOWED_ATTACHMENT_HOSTS = new Set([
|
|
88
|
+
'cdn.discordapp.com',
|
|
89
|
+
'media.discordapp.net',
|
|
90
|
+
]);
|
|
91
|
+
function isAllowedAttachmentHost(urlStr) {
|
|
92
|
+
let u;
|
|
93
|
+
try { u = new URL(urlStr); } catch { return false; }
|
|
94
|
+
if (u.protocol !== 'https:') return false;
|
|
95
|
+
return ALLOWED_ATTACHMENT_HOSTS.has(u.hostname.toLowerCase());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// PURE โ confirm the bytes really are the claimed image format (magic bytes),
|
|
99
|
+
// so a mislabeled content-type can't smuggle a non-image onto disk.
|
|
100
|
+
function sniffMatches(buf, ext) {
|
|
101
|
+
if (!buf || buf.length < 12) return false;
|
|
102
|
+
switch (ext) {
|
|
103
|
+
case 'png':
|
|
104
|
+
return buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47;
|
|
105
|
+
case 'jpg':
|
|
106
|
+
return buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff;
|
|
107
|
+
case 'gif':
|
|
108
|
+
return buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x38;
|
|
109
|
+
case 'webp':
|
|
110
|
+
return (
|
|
111
|
+
buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 && // 'RIFF'
|
|
112
|
+
buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50 // 'WEBP'
|
|
113
|
+
);
|
|
114
|
+
default:
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Download one image and re-host it. Returns { ok:true, file, url, bytes } on
|
|
120
|
+
// success, or { ok:false, reason } when the image is refused (never throws on a
|
|
121
|
+
// refusal; throws only on an unexpected fs error the caller wraps). `messageId`
|
|
122
|
+
// just seeds the (still-random) filename so attachments group readably.
|
|
123
|
+
// How many manual redirect hops we'll re-validate + follow before giving up.
|
|
124
|
+
// Discord CDN attachments are direct (0 redirects) in practice; a small bound
|
|
125
|
+
// covers a CDN edge bounce without becoming an open redirect-follow loop.
|
|
126
|
+
const MAX_REDIRECT_HOPS = 3;
|
|
127
|
+
|
|
128
|
+
async function persistImageFromUrl(url, { messageId = 'x', fetchImpl = globalThis.fetch, dir = attachmentsDir() } = {}) {
|
|
129
|
+
if (!isAllowedAttachmentHost(url)) return { ok: false, reason: 'bad_host' };
|
|
130
|
+
// L3 residual (red-team 2026-06-12): fetch with redirect:'manual' and re-run
|
|
131
|
+
// the host allowlist on every Location before following. With redirect:'follow'
|
|
132
|
+
// a 30x from an allowed host to an arbitrary host would be followed silently โ
|
|
133
|
+
// re-opening the SSRF hole the host check was meant to close (defense in depth).
|
|
134
|
+
let resp;
|
|
135
|
+
let currentUrl = url;
|
|
136
|
+
let hops = 0;
|
|
137
|
+
for (;;) {
|
|
138
|
+
try {
|
|
139
|
+
resp = await fetchImpl(currentUrl, { redirect: 'manual' });
|
|
140
|
+
} catch (e) {
|
|
141
|
+
return { ok: false, reason: 'fetch_failed', error: e.message };
|
|
142
|
+
}
|
|
143
|
+
const status = resp && resp.status;
|
|
144
|
+
if (status >= 300 && status < 400) {
|
|
145
|
+
const location = resp.headers?.get?.('location');
|
|
146
|
+
if (!location) return { ok: false, reason: 'redirect_no_location' };
|
|
147
|
+
// Resolve relative redirects against the current URL, then re-validate the
|
|
148
|
+
// RESOLVED host against the SAME allowlist before following it.
|
|
149
|
+
let next;
|
|
150
|
+
try { next = new URL(location, currentUrl).toString(); } catch { return { ok: false, reason: 'redirect_bad_location' }; }
|
|
151
|
+
if (!isAllowedAttachmentHost(next)) return { ok: false, reason: 'redirect_bad_host' };
|
|
152
|
+
if (++hops > MAX_REDIRECT_HOPS) return { ok: false, reason: 'too_many_redirects' };
|
|
153
|
+
currentUrl = next;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
if (!resp || !resp.ok) return { ok: false, reason: 'fetch_status' };
|
|
159
|
+
|
|
160
|
+
const rawType = (resp.headers?.get?.('content-type') || '').split(';')[0].trim().toLowerCase();
|
|
161
|
+
const ext = EXT_BY_TYPE[rawType];
|
|
162
|
+
if (!ext) return { ok: false, reason: 'bad_type', type: rawType };
|
|
163
|
+
|
|
164
|
+
const ab = await resp.arrayBuffer();
|
|
165
|
+
const buf = Buffer.from(ab);
|
|
166
|
+
if (buf.length === 0) return { ok: false, reason: 'empty' };
|
|
167
|
+
if (buf.length > MAX_BYTES) return { ok: false, reason: 'too_big', bytes: buf.length };
|
|
168
|
+
if (!sniffMatches(buf, ext)) return { ok: false, reason: 'sniff_mismatch' };
|
|
169
|
+
|
|
170
|
+
const idSlug = String(messageId).replace(/[^a-z0-9]/gi, '').slice(0, 40).toLowerCase() || 'x';
|
|
171
|
+
const file = `bug-${idSlug}-${crypto.randomBytes(8).toString('hex')}.${ext}`;
|
|
172
|
+
await fs.mkdir(dir, { recursive: true });
|
|
173
|
+
await fs.writeFile(path.join(dir, file), buf, { mode: 0o640 });
|
|
174
|
+
return { ok: true, file, url: `/api/gds/bug-attachments/${file}`, bytes: buf.length };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Persist a list of image URLs (capped). Best-effort: refused/failed images are
|
|
178
|
+
// skipped (logged), never aborting the others. Returns the stored refs
|
|
179
|
+
// ({ file, url }) in input order. Used by discord-bugs.recordInboundBug.
|
|
180
|
+
async function persistImages(urls, { messageId = 'x', fetchImpl = globalThis.fetch } = {}) {
|
|
181
|
+
const capped = (Array.isArray(urls) ? urls : []).slice(0, MAX_IMAGES_PER_BUG);
|
|
182
|
+
const out = [];
|
|
183
|
+
for (const u of capped) {
|
|
184
|
+
let r;
|
|
185
|
+
try {
|
|
186
|
+
r = await persistImageFromUrl(u, { messageId, fetchImpl });
|
|
187
|
+
} catch (e) {
|
|
188
|
+
console.error('[discord-bot] image persist threw (skipping):', e.message);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (r.ok) out.push({ file: r.file, url: r.url });
|
|
192
|
+
else console.warn(`[discord-bot] image refused (${r.reason}) โ skipping one attachment.`);
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
EXT_BY_TYPE,
|
|
199
|
+
TYPE_BY_EXT,
|
|
200
|
+
MAX_BYTES,
|
|
201
|
+
MAX_IMAGES_PER_BUG,
|
|
202
|
+
attachmentsDir,
|
|
203
|
+
isSafeName,
|
|
204
|
+
resolveSafe,
|
|
205
|
+
contentTypeForName,
|
|
206
|
+
isAllowedAttachmentHost,
|
|
207
|
+
sniffMatches,
|
|
208
|
+
persistImageFromUrl,
|
|
209
|
+
persistImages,
|
|
210
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// modules/discord/db.js โ the discord module's data layer (BV1.R47 / task 1416).
|
|
2
|
+
//
|
|
3
|
+
// Two halves, behind ONE `db` surface so the moved discord-*.js files keep their
|
|
4
|
+
// existing `const db = require('./db')` call sites unchanged:
|
|
5
|
+
//
|
|
6
|
+
// โข DOMAIN queries the module OWNS โ the discord_links / discord_applause
|
|
7
|
+
// tables (created by migration 067) + the discord-bug-filing count over the
|
|
8
|
+
// shared audit_log. These were lifted verbatim from src/bongos/db.js; the module
|
|
9
|
+
// reaches Postgres through the published doorway's `pool` (ADR 0083 โ a module
|
|
10
|
+
// never requires a core internal like ../../src/bongos/db).
|
|
11
|
+
//
|
|
12
|
+
// โข KERNEL db helpers the module CONSUMES โ insertAuditLog / createTask /
|
|
13
|
+
// getBuilderById + the LIVE_RANK_LADDER constant โ re-exported straight off
|
|
14
|
+
// the doorway so a single `db.*` namespace serves both.
|
|
15
|
+
//
|
|
16
|
+
// The boundary: this file requires ONLY src/module-api.js. The three DISCORD-ONLY
|
|
17
|
+
// queries (getBuilderByDiscordUserId / listDiscordLinks / countRecentDiscordBugFilings)
|
|
18
|
+
// were removed from src/bongos/db.js โ they live here now. getDiscordLink +
|
|
19
|
+
// recordApplause are INTENTIONALLY duplicated: core keeps its copies (the OAuth
|
|
20
|
+
// link flow in routes/auth.js + a seed still call them) and this module owns its
|
|
21
|
+
// own (a module can't import core db). Both read the same discord_links /
|
|
22
|
+
// discord_applause tables.
|
|
23
|
+
|
|
24
|
+
'use strict';
|
|
25
|
+
|
|
26
|
+
const api = require('../../src/module-api');
|
|
27
|
+
const { pool } = api;
|
|
28
|
+
|
|
29
|
+
// --- domain: the discord_links / discord_applause tables this module owns ----
|
|
30
|
+
|
|
31
|
+
async function getBuilderByDiscordUserId(discordUserId) {
|
|
32
|
+
const { rows } = await pool.query(
|
|
33
|
+
`SELECT b.id, b.github_login, b.display_name, b.rank, b.status, b.system_role
|
|
34
|
+
FROM discord_links dl
|
|
35
|
+
JOIN builders b ON b.id = dl.builder_id
|
|
36
|
+
WHERE dl.discord_user_id = $1`,
|
|
37
|
+
[discordUserId]
|
|
38
|
+
);
|
|
39
|
+
return rows[0] || null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function listDiscordLinks() {
|
|
43
|
+
const { rows } = await pool.query(
|
|
44
|
+
`SELECT builder_id, discord_user_id FROM discord_links ORDER BY builder_id`
|
|
45
|
+
);
|
|
46
|
+
return rows;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function getDiscordLink(builderId) {
|
|
50
|
+
const { rows } = await pool.query(
|
|
51
|
+
`SELECT builder_id, discord_user_id, linked_at FROM discord_links WHERE builder_id = $1`,
|
|
52
|
+
[builderId]
|
|
53
|
+
);
|
|
54
|
+
return rows[0] || null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function countRecentDiscordBugFilings(builderId, sinceIso) {
|
|
58
|
+
const { rows } = await pool.query(
|
|
59
|
+
`SELECT count(*)::int AS n
|
|
60
|
+
FROM audit_log
|
|
61
|
+
WHERE builder_id = $1
|
|
62
|
+
AND route = 'discord:bugs'
|
|
63
|
+
AND response_status = 201
|
|
64
|
+
AND recorded_at > $2`,
|
|
65
|
+
[builderId, sinceIso]
|
|
66
|
+
);
|
|
67
|
+
return rows[0].n;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function recordApplause({ targetTaskId, discordUserId }) {
|
|
71
|
+
const { rowCount } = await pool.query(
|
|
72
|
+
`INSERT INTO discord_applause (target_task_id, discord_user_id)
|
|
73
|
+
VALUES ($1, $2)
|
|
74
|
+
ON CONFLICT (target_task_id, discord_user_id) DO NOTHING`,
|
|
75
|
+
[targetTaskId, discordUserId]
|
|
76
|
+
);
|
|
77
|
+
return { counted: rowCount > 0 };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
// domain (this module's tables)
|
|
82
|
+
getBuilderByDiscordUserId,
|
|
83
|
+
listDiscordLinks,
|
|
84
|
+
getDiscordLink,
|
|
85
|
+
countRecentDiscordBugFilings,
|
|
86
|
+
recordApplause,
|
|
87
|
+
// kernel db helpers, re-exported off the doorway (one `db.*` namespace)
|
|
88
|
+
insertAuditLog: (...a) => api.insertAuditLog(...a),
|
|
89
|
+
// createTask is a LIFECYCLE write โ resolve the `lifecycle` kernel port at call
|
|
90
|
+
// time (the doorway leak retired at BV1.R86); used by discord-bugs (#bugs โ task).
|
|
91
|
+
createTask: (...a) => api.resolveOptional('lifecycle').createTask(...a),
|
|
92
|
+
getBuilderById: (...a) => api.getBuilderById(...a),
|
|
93
|
+
get LIVE_RANK_LADDER() { return api.LIVE_RANK_LADDER; },
|
|
94
|
+
// the shared pool, for any module file that wants it via db.pool
|
|
95
|
+
pool,
|
|
96
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Discord reaction โ Archon approval (task 1100).
|
|
4
|
+
//
|
|
5
|
+
// An Archon's ๐/๐ on a request line in #member-approvals fires the SAME server
|
|
6
|
+
// action the amazonprimea hall fires:
|
|
7
|
+
// #member-approvals ๐ โ access_requests โ 'invited' (PATCH /access-requests/:id)
|
|
8
|
+
// ๐ โ access_requests โ 'dismissed'
|
|
9
|
+
//
|
|
10
|
+
// TRUST BOUNDARY (ADR 0016 / 0033 ยง1) โ the one invariant: every bot-triggered
|
|
11
|
+
// write re-checks the LINKED builder's LIVE rank. The bot asserts only WHO
|
|
12
|
+
// reacted (Discord user โ linked builder via the F1 table, which reads rank
|
|
13
|
+
// FRESH from `builders`); the GDS decides WHAT they may do. Both these actions
|
|
14
|
+
// are Archon-only on the web (auth.requireRank('archon')), so here we re-derive
|
|
15
|
+
// the reactor's live rank and act ONLY when it is 'archon'. Anything else (an
|
|
16
|
+
// unlinked reactor, a Metic, a player) is a no-op. The bot cannot escalate; the
|
|
17
|
+
// worst a compromised bot could do is mis-report which Archon reacted, bounded
|
|
18
|
+
// by the audit_log (surface='discord_bot').
|
|
19
|
+
//
|
|
20
|
+
// WHY A FUNCTION, NOT AN HTTP ENDPOINT: the bot is in-process (ADR 0033 F3), so
|
|
21
|
+
// it calls this directly โ no localhost self-HTTP, no minted bot session token.
|
|
22
|
+
//
|
|
23
|
+
// FAIL-CLOSED + NEVER THROWS: the caller is a gateway event handler that must
|
|
24
|
+
// not crash the bot; every path returns { outcome } and all failure is caught.
|
|
25
|
+
|
|
26
|
+
const { pool } = require('../../src/module-api');
|
|
27
|
+
const db = require('./db');
|
|
28
|
+
const { auditDiscordBot } = require('./discord-audit');
|
|
29
|
+
const auth = require('./auth');
|
|
30
|
+
|
|
31
|
+
// Only the top rank may approve admissions (identical to the
|
|
32
|
+
// web routes' auth.requireRank('archon')).
|
|
33
|
+
const APPROVING_RANK = 'archon';
|
|
34
|
+
|
|
35
|
+
// PURE โ map a discord.js emoji name to an approval decision, or null for any
|
|
36
|
+
// other reaction (which the handler then ignores).
|
|
37
|
+
function emojiToDecision(emojiName) {
|
|
38
|
+
if (emojiName === '๐') return 'approve';
|
|
39
|
+
if (emojiName === '๐') return 'decline';
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// PURE โ extract { kind, id } from an approval request line. The producer
|
|
44
|
+
// (approval-broadcast.js) LEADS each line with a `member-request #<id>` token; we
|
|
45
|
+
// anchor to the start so a body that mentions another "#7" can't hijack the
|
|
46
|
+
// mapping. kind is always 'member'. Returns null when the line carries no leading
|
|
47
|
+
// approval marker (e.g. a confirmation reply we post).
|
|
48
|
+
function parseApprovalMarker(content) {
|
|
49
|
+
if (typeof content !== 'string') return null;
|
|
50
|
+
const m = /^\s*(member-request)\s+#(\d{1,15})\b/.exec(content);
|
|
51
|
+
if (!m) return null;
|
|
52
|
+
const id = Number(m[2]);
|
|
53
|
+
if (!Number.isInteger(id) || id <= 0) return null;
|
|
54
|
+
return { kind: 'member', id };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// DB โ resolve a pending access request to 'invited' | 'dismissed', stamping the
|
|
58
|
+
// approving Archon. GUARDED on status='pending' (idempotent: a second reaction,
|
|
59
|
+
// or one on an already-resolved request, matches nothing โ null), exactly like
|
|
60
|
+
// the PATCH /access-requests/:id route. Returns the updated row or null.
|
|
61
|
+
async function resolveAccessRequest({ id, status, builderId }) {
|
|
62
|
+
const { rows } = await pool.query(
|
|
63
|
+
`UPDATE access_requests
|
|
64
|
+
SET status = $1, resolved_by_builder_id = $2, resolved_at = now()
|
|
65
|
+
WHERE id = $3 AND status = 'pending'
|
|
66
|
+
RETURNING id, github_login, status`,
|
|
67
|
+
[status, builderId, id]
|
|
68
|
+
);
|
|
69
|
+
return rows[0] || null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// The real dependency set โ injected so recordApprovalReaction is unit-testable
|
|
73
|
+
// against fakes with NO database.
|
|
74
|
+
const realDeps = {
|
|
75
|
+
getBuilderByDiscordUserId: db.getBuilderByDiscordUserId,
|
|
76
|
+
resolveAccessRequest,
|
|
77
|
+
insertAuditLog: db.insertAuditLog,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Orchestrate one approval reaction. NEVER throws. `kind` is fixed by WHICH
|
|
81
|
+
// channel the reaction landed in (the bot knows the channel id) and is always
|
|
82
|
+
// 'member'; `decision` ('approve'|'decline') comes from the emoji; `recordId`
|
|
83
|
+
// from the parsed marker. Returns { outcome, ... } where outcome โ
|
|
84
|
+
// 'approved' | 'declined' โ action fired
|
|
85
|
+
// 'already_resolved' โ record was gone/resolved (idempotent no-op)
|
|
86
|
+
// 'ignored' (+reason) โ not an Archon / bad input
|
|
87
|
+
// 'error' (+error)
|
|
88
|
+
async function recordApprovalReaction({ discordUserId, kind, decision, recordId }, deps = realDeps) {
|
|
89
|
+
if (!auth.isValidDiscordUserId(discordUserId)) {
|
|
90
|
+
return { outcome: 'ignored', reason: 'bad_discord_user_id' };
|
|
91
|
+
}
|
|
92
|
+
if (kind !== 'member') {
|
|
93
|
+
return { outcome: 'ignored', reason: 'bad_kind' };
|
|
94
|
+
}
|
|
95
|
+
if (decision !== 'approve' && decision !== 'decline') {
|
|
96
|
+
return { outcome: 'ignored', reason: 'bad_decision' };
|
|
97
|
+
}
|
|
98
|
+
if (!Number.isInteger(recordId) || recordId <= 0) {
|
|
99
|
+
return { outcome: 'ignored', reason: 'bad_record_id' };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
// Re-derive the reactor's LIVE rank โ the whole trust boundary (ADR 0016).
|
|
104
|
+
const builder = await deps.getBuilderByDiscordUserId(discordUserId);
|
|
105
|
+
if (!builder) return { outcome: 'ignored', reason: 'unlinked_reactor' };
|
|
106
|
+
if (builder.rank !== APPROVING_RANK) return { outcome: 'ignored', reason: 'not_archon' };
|
|
107
|
+
|
|
108
|
+
const status = decision === 'approve' ? 'invited' : 'dismissed';
|
|
109
|
+
const resolved = await deps.resolveAccessRequest({ id: recordId, status, builderId: builder.id });
|
|
110
|
+
|
|
111
|
+
// A guarded UPDATE matched nothing โ the record was already resolved or gone.
|
|
112
|
+
// Idempotent no-op (a duplicate/late reaction), not an error.
|
|
113
|
+
if (!resolved) {
|
|
114
|
+
return { outcome: 'already_resolved', kind, recordId, builderId: builder.id };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const outcome = decision === 'approve' ? 'approved' : 'declined';
|
|
118
|
+
|
|
119
|
+
// Audit every bot-triggered write (ADR 0033 ยง1). Best-effort โ an audit
|
|
120
|
+
// failure must not undo the approval.
|
|
121
|
+
await auditDiscordBot(deps, {
|
|
122
|
+
builderId: builder.id,
|
|
123
|
+
route: `discord:approval:${kind}`,
|
|
124
|
+
requestBodyRedacted: { discord_user_id: discordUserId, kind, record_id: recordId, decision, outcome },
|
|
125
|
+
responseStatus: 200,
|
|
126
|
+
logLabel: 'approval audit-log write failed',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return { outcome, kind, recordId, builderId: builder.id, resolved };
|
|
130
|
+
} catch (err) {
|
|
131
|
+
console.error('[discord-bot] recordApprovalReaction failed:', err.message);
|
|
132
|
+
return { outcome: 'error', error: err.message };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = {
|
|
137
|
+
// pure (unit-tested)
|
|
138
|
+
emojiToDecision,
|
|
139
|
+
parseApprovalMarker,
|
|
140
|
+
APPROVING_RANK,
|
|
141
|
+
// orchestration + DB
|
|
142
|
+
recordApprovalReaction,
|
|
143
|
+
resolveAccessRequest,
|
|
144
|
+
realDeps,
|
|
145
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/bongos/discord-audit.js
|
|
2
|
+
//
|
|
3
|
+
// Shared wrapper for the bot's audit-log writes (task 1091, C7 โ dedup audit
|
|
4
|
+
// 2026-06-14 ยง2A discord). Every bot-triggered write is audited (ADR 0033 ยง1)
|
|
5
|
+
// with method='POST' and surface='discord_bot', best-effort: an audit failure
|
|
6
|
+
// must NEVER undo or throw out of the action it records, so it is caught and
|
|
7
|
+
// logged non-fatally. The five call sites (approvals, bugs ร2, inbound,
|
|
8
|
+
// reaction) differ only in builderId / route / requestBodyRedacted /
|
|
9
|
+
// responseStatus and the log label โ everything else is fixed here.
|
|
10
|
+
//
|
|
11
|
+
// `deps` carries the injected insertAuditLog (deps.insertAuditLog) so the
|
|
12
|
+
// existing unit tests' mocked deps continue to observe the identical call.
|
|
13
|
+
|
|
14
|
+
async function auditDiscordBot(deps, { builderId, route, requestBodyRedacted, responseStatus, logLabel }) {
|
|
15
|
+
try {
|
|
16
|
+
await deps.insertAuditLog({
|
|
17
|
+
builderId,
|
|
18
|
+
route,
|
|
19
|
+
method: 'POST',
|
|
20
|
+
requestBodyRedacted,
|
|
21
|
+
responseStatus,
|
|
22
|
+
surface: 'discord_bot',
|
|
23
|
+
});
|
|
24
|
+
} catch (auditErr) {
|
|
25
|
+
console.error(`[discord-bot] ${logLabel} (non-fatal):`, auditErr.message);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = { auditDiscordBot };
|