@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,137 @@
|
|
|
1
|
+
// tests/main_audit.mjs
|
|
2
|
+
//
|
|
3
|
+
// Unit smoke for scripts/gds/main-audit.js — the post-push main audit
|
|
4
|
+
// (SEC #863, layer 3). Pure analyzer: feed synthetic commits + a rank map.
|
|
5
|
+
//
|
|
6
|
+
// Run: node tests/main_audit.mjs
|
|
7
|
+
|
|
8
|
+
import { strict as assert } from 'node:assert';
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
10
|
+
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const { analyzeCommits } = require('../scripts/gds/main-audit.js');
|
|
13
|
+
|
|
14
|
+
let passed = 0, failed = 0;
|
|
15
|
+
function t(name, fn) {
|
|
16
|
+
try { fn(); passed++; console.log(` PASS ${name}`); }
|
|
17
|
+
catch (e) { failed++; console.log(` FAIL ${name}\n ${e.message}`); }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const RANKS = {
|
|
21
|
+
iammasterqua: 'thetes', // Builder 25 (incident author — not the Archon)
|
|
22
|
+
somearchon: 'archon',
|
|
23
|
+
somemetic: 'metic',
|
|
24
|
+
somexenos: 'xenos',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// The exact incident: a Thetes non-merge commit editing src/bongos/db.js.
|
|
28
|
+
t('reconstructs #863: thetes commit touching db.js → BLOCKER', () => {
|
|
29
|
+
const commits = [{
|
|
30
|
+
sha: 'b5de8affbf6f5fedc6c0921cfb35c8cbf85f7def',
|
|
31
|
+
subject: 'fix(gds): add thetes to rank-tier CASE in listClaimableTasks',
|
|
32
|
+
authorName: 'IAMMASTERQUA',
|
|
33
|
+
authorEmail: 'lars@hermeslines.com',
|
|
34
|
+
isMerge: false,
|
|
35
|
+
files: ['src/bongos/db.js', 'tests/rank_gate.mjs'],
|
|
36
|
+
}];
|
|
37
|
+
const r = analyzeCommits(commits, RANKS);
|
|
38
|
+
assert.equal(r.hardFail, true);
|
|
39
|
+
assert.equal(r.findings.length, 1);
|
|
40
|
+
assert.equal(r.findings[0].severity, 'blocker');
|
|
41
|
+
assert.equal(r.findings[0].rank, 'thetes');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
t('Metic editing a protected path → no finding', () => {
|
|
45
|
+
const r = analyzeCommits([{
|
|
46
|
+
sha: 'aaa', subject: 'x', authorName: 'SomeMetic', authorEmail: 'm@x',
|
|
47
|
+
isMerge: false, files: ['src/bongos/db.js'],
|
|
48
|
+
}], RANKS);
|
|
49
|
+
assert.equal(r.hardFail, false);
|
|
50
|
+
assert.equal(r.findings.length, 0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
t('Archon editing a protected path → no finding', () => {
|
|
54
|
+
const r = analyzeCommits([{
|
|
55
|
+
sha: 'bbb', subject: 'x', authorName: 'somearchon', authorEmail: 'l@x',
|
|
56
|
+
isMerge: false, files: ['scripts/gds/ship.js', 'CLAUDE.md'],
|
|
57
|
+
}], RANKS);
|
|
58
|
+
assert.equal(r.hardFail, false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
t('sub-Metic editing only innocent files → no finding', () => {
|
|
62
|
+
const r = analyzeCommits([{
|
|
63
|
+
sha: 'ccc', subject: 'art', authorName: 'somexenos', authorEmail: 'x@x',
|
|
64
|
+
isMerge: false, files: ['public/game/main.js', 'art/x.md'],
|
|
65
|
+
}], RANKS);
|
|
66
|
+
assert.equal(r.hardFail, false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
t('clean MERGE (empty --cc surface) → no finding (no double-count)', () => {
|
|
70
|
+
// A normal pipeline merge has an empty evil-merge (--cc) set; parseCommits
|
|
71
|
+
// leaves files=[] so the feature changes are attributed to the feature
|
|
72
|
+
// commits, not re-counted on the merge.
|
|
73
|
+
const r = analyzeCommits([{
|
|
74
|
+
sha: 'ddd', subject: 'Merge claude/x', authorName: 'IAMMASTERQUA', authorEmail: 'i@x',
|
|
75
|
+
isMerge: true, files: [],
|
|
76
|
+
}], RANKS);
|
|
77
|
+
assert.equal(r.findings.length, 0, 'clean merges must not flag');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
t('#863-followup: EVIL MERGE smuggling a protected change is caught (attributed to merge author)', () => {
|
|
81
|
+
// parseCommits backfills a merge's files with the --cc surface (changes in
|
|
82
|
+
// neither parent). A sub-Metic merge author smuggling a protected edit → blocker.
|
|
83
|
+
const r = analyzeCommits([{
|
|
84
|
+
sha: 'eee', subject: 'Merge claude/x', authorName: 'IAMMASTERQUA', authorEmail: 'i@x',
|
|
85
|
+
isMerge: true, files: ['src/bongos/db.js'],
|
|
86
|
+
}], RANKS);
|
|
87
|
+
assert.equal(r.hardFail, true, 'evil merge by a sub-Metic author must be a blocker');
|
|
88
|
+
assert.equal(r.findings[0].severity, 'blocker');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
t('unresolved author on protected path → review (not a block) by default', () => {
|
|
92
|
+
// The trusted operator/deployer commits under a git author NAME that is not a
|
|
93
|
+
// GDS login, so unresolved must NOT auto-block (would brick every deploy).
|
|
94
|
+
const r = analyzeCommits([{
|
|
95
|
+
sha: 'eee', subject: 'x', authorName: 'ghost-contributor', authorEmail: 'g@x',
|
|
96
|
+
isMerge: false, files: ['src/bongos/auth.js'],
|
|
97
|
+
}], RANKS);
|
|
98
|
+
assert.equal(r.hardFail, false);
|
|
99
|
+
assert.equal(r.findings.length, 1);
|
|
100
|
+
assert.equal(r.findings[0].severity, 'review');
|
|
101
|
+
assert.equal(r.findings[0].rank, null);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
t('unresolved author escalates to BLOCKER under --strict', () => {
|
|
105
|
+
const r = analyzeCommits([{
|
|
106
|
+
sha: 'fff', subject: 'x', authorName: 'ghost', authorEmail: 'g@x',
|
|
107
|
+
isMerge: false, files: ['src/bongos/auth.js'],
|
|
108
|
+
}], RANKS, { strict: true });
|
|
109
|
+
assert.equal(r.hardFail, true);
|
|
110
|
+
assert.equal(r.findings[0].severity, 'blocker');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
t('#863-followup (finding #6): resolution is by github_login ONLY — email no longer resolves', () => {
|
|
114
|
+
// Everyone shares one commit email, so email keying was non-distinguishing
|
|
115
|
+
// (and poisonable). An author whose NAME is not a known login is unresolved,
|
|
116
|
+
// regardless of email → 'review' by default (not silently passed as Metic).
|
|
117
|
+
const r = analyzeCommits([{
|
|
118
|
+
sha: 'ggg', subject: 'x', authorName: 'Display Name Mismatch', authorEmail: 'somemetic',
|
|
119
|
+
isMerge: false, files: ['src/bongos/db.js'],
|
|
120
|
+
}], RANKS);
|
|
121
|
+
assert.equal(r.findings.length, 1);
|
|
122
|
+
assert.equal(r.findings[0].rank, null, 'email must not resolve a rank');
|
|
123
|
+
assert.equal(r.findings[0].severity, 'review');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
t('#863-followup (finding #6): a sub-Metic author NAME still resolves and BLOCKS (the incident)', () => {
|
|
127
|
+
// The honest/lazy case — niels committed as his real login "IAMMASTERQUA".
|
|
128
|
+
const r = analyzeCommits([{
|
|
129
|
+
sha: 'hhh', subject: 'x', authorName: 'IAMMASTERQUA', authorEmail: 'shared@x',
|
|
130
|
+
isMerge: false, files: ['src/bongos/db.js'],
|
|
131
|
+
}], RANKS);
|
|
132
|
+
assert.equal(r.hardFail, true);
|
|
133
|
+
assert.equal(r.findings[0].rank, 'thetes');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
console.log(`\n${passed} passed, ${failed} failed`);
|
|
137
|
+
process.exit(failed ? 1 : 0);
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
// tests/memory_isolation.mjs
|
|
2
|
+
//
|
|
3
|
+
// Unit + HTTP smoke for per-builder memory isolation (V3.R57 / #276 — the read
|
|
4
|
+
// side + isolation hardening of the ADR 0024 cloneable-memory chain).
|
|
5
|
+
//
|
|
6
|
+
// The invariant under test (ADR 0024 §4, same posture as ADR 0016): a builder
|
|
7
|
+
// can only read/write THEIR OWN memory. The owner is ALWAYS req.builder.id,
|
|
8
|
+
// NEVER from input — except the two Archon-only forensic reads, which take a
|
|
9
|
+
// target id from the path, are rank-gated, and audit-log every call.
|
|
10
|
+
//
|
|
11
|
+
// Two layers of test:
|
|
12
|
+
// 1. Pure-helper + source-structure checks (no Express, no DB):
|
|
13
|
+
// - assertSelf() — the explicit isolation invariant for self endpoints.
|
|
14
|
+
// - recordForensicRead() — the Archon audit shape + accurate status.
|
|
15
|
+
// - a source-level structural check that the self endpoints derive their
|
|
16
|
+
// owner from req.builder.id ONLY, and that the ONLY input-owner
|
|
17
|
+
// endpoints are the rank-gated, audited Archon forensic reads.
|
|
18
|
+
// 2. REAL in-process HTTP checks through the actual router (no DB): we mount
|
|
19
|
+
// buildMemoryRouter() on a throwaway express app, inject fake auth by
|
|
20
|
+
// stubbing auth.requireBuilder / auth.requireRank, stub the memory store
|
|
21
|
+
// helpers to record the builderId they were called with, stub the audit
|
|
22
|
+
// insert to record/await/throw, then fire real requests via node:http +
|
|
23
|
+
// global fetch and assert status + isolation behavior end-to-end. This is
|
|
24
|
+
// the authenticated "builder A reading builder B → 403" smoke the done-when
|
|
25
|
+
// requires, plus the fail-closed-audit proof.
|
|
26
|
+
//
|
|
27
|
+
// Run: node tests/memory_isolation.mjs
|
|
28
|
+
|
|
29
|
+
import { strict as assert } from 'node:assert';
|
|
30
|
+
import { createRequire } from 'node:module';
|
|
31
|
+
import http from 'node:http';
|
|
32
|
+
import fs from 'node:fs';
|
|
33
|
+
import path from 'node:path';
|
|
34
|
+
import url from 'node:url';
|
|
35
|
+
|
|
36
|
+
const require = createRequire(import.meta.url);
|
|
37
|
+
|
|
38
|
+
// Grab the real module objects so we can monkeypatch their exported functions
|
|
39
|
+
// BEFORE building the router (requireBuilder / requireRank are captured at
|
|
40
|
+
// build time; memory.* and db.insertAuditLog are called by property at request
|
|
41
|
+
// time). This is the same "stub the dependency module" pattern the project uses
|
|
42
|
+
// elsewhere — no DI framework, just Node's module cache.
|
|
43
|
+
const auth = require('../src/module-api'); // doorway: memory-module routes capture api.requireBuilder, so the stub must patch the doorway (BV1.R72 / ADR 0091)
|
|
44
|
+
const memory = require('../modules/memory/memory.js');
|
|
45
|
+
const db = require('../src/bongos/db.js');
|
|
46
|
+
|
|
47
|
+
let passed = 0;
|
|
48
|
+
let failed = 0;
|
|
49
|
+
function t(name, fn) {
|
|
50
|
+
return Promise.resolve()
|
|
51
|
+
.then(fn)
|
|
52
|
+
.then(() => { passed++; console.log(` PASS ${name}`); })
|
|
53
|
+
.catch((e) => { failed++; console.log(` FAIL ${name}\n ${e.message}`); });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ===========================================================================
|
|
57
|
+
// Layer 1 — pure helpers + source structure
|
|
58
|
+
// ===========================================================================
|
|
59
|
+
|
|
60
|
+
const buildMemoryRouter = require('../modules/memory/routes/memory.js');
|
|
61
|
+
const { assertSelf, recordForensicRead } = buildMemoryRouter._internals;
|
|
62
|
+
|
|
63
|
+
console.log('assertSelf (isolation invariant for the self endpoints):');
|
|
64
|
+
|
|
65
|
+
await t('returns the session builder id when no owner is claimed', () => {
|
|
66
|
+
const id = assertSelf({ builder: { id: 42 } });
|
|
67
|
+
assert.equal(id, 42);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await t('returns the session id when a matching owner is claimed', () => {
|
|
71
|
+
const id = assertSelf({ builder: { id: 42 } }, 42);
|
|
72
|
+
assert.equal(id, 42);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await t('THROWS CROSS_BUILDER_FORBIDDEN when a DIFFERENT owner is claimed', () => {
|
|
76
|
+
assert.throws(
|
|
77
|
+
() => assertSelf({ builder: { id: 42 } }, 99),
|
|
78
|
+
(e) => e && e.code === 'CROSS_BUILDER_FORBIDDEN' && String(e.self) === '42' && String(e.claimed) === '99'
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await t('THROWS CROSS_BUILDER_FORBIDDEN even when ids differ only by string/number type', () => {
|
|
83
|
+
assert.throws(() => assertSelf({ builder: { id: 42 } }, '99'), (e) => e.code === 'CROSS_BUILDER_FORBIDDEN');
|
|
84
|
+
assert.equal(assertSelf({ builder: { id: 42 } }, '42'), 42);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await t('fails closed (NOT_AUTHENTICATED) when there is no builder on the request', () => {
|
|
88
|
+
assert.throws(() => assertSelf({}), (e) => e && e.code === 'NOT_AUTHENTICATED');
|
|
89
|
+
assert.throws(() => assertSelf({ builder: {} }), (e) => e && e.code === 'NOT_AUTHENTICATED');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
console.log('\nrecordForensicRead (Archon cross-builder reads are audit-logged):');
|
|
93
|
+
|
|
94
|
+
await t('writes an audit row with the archon id, target id, path, and forensic action', async () => {
|
|
95
|
+
let captured = null;
|
|
96
|
+
const req = {
|
|
97
|
+
builder: { id: 7, rank: 'archon' },
|
|
98
|
+
ip: '203.0.113.9',
|
|
99
|
+
headers: { 'user-agent': 'gds-cli/1.0' },
|
|
100
|
+
};
|
|
101
|
+
await recordForensicRead(
|
|
102
|
+
req,
|
|
103
|
+
{ route: '/api/gds/memory/builders/:builderId/file', targetBuilderId: 99, target: 'MEMORY.md' },
|
|
104
|
+
(row) => { captured = row; }
|
|
105
|
+
);
|
|
106
|
+
assert.ok(captured, 'an audit row should have been built');
|
|
107
|
+
assert.equal(captured.builderId, 7, 'audit row attributes the read to the ARCHON');
|
|
108
|
+
assert.ok(['POST', 'PATCH', 'PUT', 'DELETE'].includes(captured.method), 'method must satisfy the CHECK constraint');
|
|
109
|
+
assert.equal(captured.route, '/api/gds/memory/builders/:builderId/file');
|
|
110
|
+
assert.equal(captured.requestBodyRedacted.action, 'archon_forensic_memory_read');
|
|
111
|
+
assert.equal(captured.requestBodyRedacted.http_method, 'GET');
|
|
112
|
+
assert.equal(captured.requestBodyRedacted.target_builder_id, 99);
|
|
113
|
+
assert.equal(captured.requestBodyRedacted.target, 'MEMORY.md');
|
|
114
|
+
assert.equal(captured.ip, '203.0.113.9');
|
|
115
|
+
assert.equal(captured.userAgent, 'gds-cli/1.0');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await t('records the ACTUAL outcome status (404 when the target file was absent)', async () => {
|
|
119
|
+
// Fix #4: the audit row must carry the real outcome, not a hardcoded 200.
|
|
120
|
+
let captured = null;
|
|
121
|
+
await recordForensicRead(
|
|
122
|
+
{ builder: { id: 7 }, headers: {} },
|
|
123
|
+
{ route: '/api/gds/memory/builders/:builderId/file', targetBuilderId: 99, target: 'gone.md', responseStatus: 404 },
|
|
124
|
+
(row) => { captured = row; }
|
|
125
|
+
);
|
|
126
|
+
assert.equal(captured.responseStatus, 404, 'a 404 look must be audited as 404');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await t('defaults responseStatus to 200 when not specified', async () => {
|
|
130
|
+
let captured = null;
|
|
131
|
+
await recordForensicRead(
|
|
132
|
+
{ builder: { id: 7 }, headers: {} },
|
|
133
|
+
{ route: '/api/gds/memory/builders/:builderId/files', targetBuilderId: 99, target: 'list' },
|
|
134
|
+
(row) => { captured = row; }
|
|
135
|
+
);
|
|
136
|
+
assert.equal(captured.responseStatus, 200);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
await t('uses target="list" for the directory-listing forensic read', async () => {
|
|
140
|
+
let captured = null;
|
|
141
|
+
await recordForensicRead(
|
|
142
|
+
{ builder: { id: 7 }, headers: {} },
|
|
143
|
+
{ route: '/api/gds/memory/builders/:builderId/files', targetBuilderId: 99, target: 'list' },
|
|
144
|
+
(row) => { captured = row; }
|
|
145
|
+
);
|
|
146
|
+
assert.equal(captured.requestBodyRedacted.target, 'list');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
await t('PROPAGATES an audit insert failure (fail-closed: caller must see it)', async () => {
|
|
150
|
+
// Fix #1: recordForensicRead no longer swallows. The route catches the
|
|
151
|
+
// rejection and fails the read closed (500) — proven end-to-end below — but
|
|
152
|
+
// at the helper level the contract is now "reject, do not swallow".
|
|
153
|
+
let threw = false;
|
|
154
|
+
try {
|
|
155
|
+
await recordForensicRead(
|
|
156
|
+
{ builder: { id: 7 }, headers: {} },
|
|
157
|
+
{ route: '/api/gds/memory/builders/:builderId/files', targetBuilderId: 99, target: 'list' },
|
|
158
|
+
() => { throw new Error('db down'); }
|
|
159
|
+
);
|
|
160
|
+
} catch (_e) {
|
|
161
|
+
threw = true;
|
|
162
|
+
}
|
|
163
|
+
assert.equal(threw, true, 'recordForensicRead MUST reject on insert failure so the route can fail closed');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
console.log('\nroute source structural checks (the trust boundary is airtight):');
|
|
167
|
+
|
|
168
|
+
const here = path.dirname(url.fileURLToPath(import.meta.url));
|
|
169
|
+
const rawSrc = fs.readFileSync(path.join(here, '..', 'modules', 'memory', 'routes', 'memory.js'), 'utf8');
|
|
170
|
+
const src = rawSrc; // route/rank-gate checks run against the full source
|
|
171
|
+
const codeOnly = rawSrc
|
|
172
|
+
.split('\n')
|
|
173
|
+
.map((line) => {
|
|
174
|
+
const idx = line.indexOf('//');
|
|
175
|
+
return idx >= 0 ? line.slice(0, idx) : line;
|
|
176
|
+
})
|
|
177
|
+
.join('\n');
|
|
178
|
+
|
|
179
|
+
await t('the two Archon forensic endpoints are both rank-gated to archon', () => {
|
|
180
|
+
const forensicRoutes = [
|
|
181
|
+
"'/memory/builders/:builderId/files'",
|
|
182
|
+
"'/memory/builders/:builderId/file'",
|
|
183
|
+
];
|
|
184
|
+
for (const route of forensicRoutes) {
|
|
185
|
+
const idx = src.indexOf(route);
|
|
186
|
+
assert.ok(idx >= 0, `route ${route} should be declared`);
|
|
187
|
+
const window = src.slice(idx, idx + 200);
|
|
188
|
+
assert.ok(
|
|
189
|
+
/requireRank\('archon'\)/.test(window),
|
|
190
|
+
`route ${route} must be gated by requireRank('archon')`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await t('every forensic endpoint calls recordForensicRead (audited)', () => {
|
|
196
|
+
const callSites = (src.match(/recordForensicRead\(req,/g) || []).length;
|
|
197
|
+
assert.ok(callSites >= 2, `expected >=2 recordForensicRead call sites, found ${callSites}`);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
await t('production code contains no grader-system references', () => {
|
|
201
|
+
// Fix #3: shipped code must not name the grader or its workers.
|
|
202
|
+
assert.ok(!/grader/i.test(rawSrc), 'memory.js route source must not mention the grader');
|
|
203
|
+
assert.ok(!/hacker worker/i.test(rawSrc), 'memory.js route source must not mention grader workers');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
await t('the SELF read endpoints never derive the owner from request input', () => {
|
|
207
|
+
assert.ok(!/req\.query\.builder/i.test(codeOnly), 'no owner should come from req.query.builder*');
|
|
208
|
+
assert.ok(!/req\.body\.builder/i.test(codeOnly), 'no owner should come from req.body.builder*');
|
|
209
|
+
assert.ok(!/req\.body\.owner/i.test(codeOnly), 'no owner should come from req.body.owner');
|
|
210
|
+
// An input-supplied owner (req.params.builderId) may ONLY appear inside a
|
|
211
|
+
// PRIVILEGED cross-builder endpoint: either the Archon-rank-gated forensic
|
|
212
|
+
// READS (requireRank('archon')) or the BFG-principal-gated WRITE
|
|
213
|
+
// (requireBfgPrincipal — an identity gate stricter than any rank; 6C.1 /
|
|
214
|
+
// ADR 0026 §6C). Both are audited. No SELF endpoint reads it.
|
|
215
|
+
const paramUses = [...codeOnly.matchAll(/req\.params\.builderId/g)].map((m) => m.index);
|
|
216
|
+
for (const i of paramUses) {
|
|
217
|
+
const window = codeOnly.slice(Math.max(0, i - 700), i);
|
|
218
|
+
assert.ok(
|
|
219
|
+
/requireRank\('archon'\)/.test(window) || /requireBfgPrincipal/.test(window),
|
|
220
|
+
'req.params.builderId may only be read inside an archon-gated forensic read OR the BFG-principal-gated write'
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
await t('listMemoryFiles / fetchMemoryFile are called with a self-scoped or audited id only', () => {
|
|
226
|
+
assert.ok(/assertSelf\(req\)/.test(src), 'self endpoints must call assertSelf(req)');
|
|
227
|
+
assert.ok(/Number\(req\.params\.builderId\)/.test(src), 'forensic endpoints must coerce + validate the target id');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// ===========================================================================
|
|
231
|
+
// Layer 2 — REAL in-process HTTP through the actual router (no DB)
|
|
232
|
+
//
|
|
233
|
+
// We monkeypatch the dependency modules, build the REAL router, mount it on a
|
|
234
|
+
// throwaway express app, listen on an ephemeral port, and fire real requests.
|
|
235
|
+
// This proves the done-when "builder A reading builder B → 403" against the
|
|
236
|
+
// actual middleware stack, plus the fail-closed-audit fix end-to-end.
|
|
237
|
+
// ===========================================================================
|
|
238
|
+
|
|
239
|
+
console.log('\nHTTP-level isolation (real router, fake auth, stubbed store/audit):');
|
|
240
|
+
|
|
241
|
+
// --- save originals so we can restore after the HTTP suite ---
|
|
242
|
+
const orig = {
|
|
243
|
+
requireBuilder: auth.requireBuilder,
|
|
244
|
+
requireRank: auth.requireRank,
|
|
245
|
+
listMemoryFiles: memory.listMemoryFiles,
|
|
246
|
+
fetchMemoryFile: memory.fetchMemoryFile,
|
|
247
|
+
insertAuditLog: db.insertAuditLog,
|
|
248
|
+
assertValidPath: memory.assertValidPath,
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// --- mutable test fixtures the stubs read/write ---
|
|
252
|
+
const fixture = {
|
|
253
|
+
// the authenticated session injected by the fake requireBuilder
|
|
254
|
+
session: { id: 1, rank: 'xenos' },
|
|
255
|
+
// record every (fn, builderId) the store helpers were called with
|
|
256
|
+
storeCalls: [],
|
|
257
|
+
// record every audit row the insert stub was handed; toggle to make it throw
|
|
258
|
+
auditCalls: [],
|
|
259
|
+
auditShouldThrow: false,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Fake auth: requireBuilder injects whatever fixture.session currently is.
|
|
263
|
+
auth.requireBuilder = function fakeRequireBuilder(req, _res, next) {
|
|
264
|
+
req.builder = { ...fixture.session };
|
|
265
|
+
next();
|
|
266
|
+
};
|
|
267
|
+
// Real-shaped requireRank: 403 if the injected session's rank isn't allowed —
|
|
268
|
+
// this is the exact gate behavior, so the 403 we assert is the real rank gate.
|
|
269
|
+
auth.requireRank = function fakeRequireRank(...allowed) {
|
|
270
|
+
const allowedSet = new Set(allowed);
|
|
271
|
+
return function rankGate(req, res, next) {
|
|
272
|
+
const actual = req.builder && req.builder.rank;
|
|
273
|
+
if (!actual || !allowedSet.has(actual)) {
|
|
274
|
+
res.status(403).json({ error: 'rank_forbidden', required: [...allowedSet], actual: actual ?? null });
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
next();
|
|
278
|
+
};
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// Stub the store helpers to RECORD the builderId they were called with (the
|
|
282
|
+
// isolation assertion) and return canned rows — no DB.
|
|
283
|
+
memory.listMemoryFiles = async function stubList(builderId) {
|
|
284
|
+
fixture.storeCalls.push({ fn: 'listMemoryFiles', builderId });
|
|
285
|
+
return [
|
|
286
|
+
{ path: 'MEMORY.md', sha256: 'abc', byte_len: 10, mtime: null, updated_at: '2026-05-30T00:00:00Z', created_at: '2026-05-30T00:00:00Z' },
|
|
287
|
+
];
|
|
288
|
+
};
|
|
289
|
+
memory.fetchMemoryFile = async function stubFetch(builderId, p) {
|
|
290
|
+
fixture.storeCalls.push({ fn: 'fetchMemoryFile', builderId, path: p });
|
|
291
|
+
if (p === 'missing.md') return null;
|
|
292
|
+
return { path: p, content: 'hello', sha256: 'abc', byte_len: 5, mtime: null, updated_at: '2026-05-30T00:00:00Z', created_at: '2026-05-30T00:00:00Z' };
|
|
293
|
+
};
|
|
294
|
+
// Keep path validation real (it's a pure function) — just pass through.
|
|
295
|
+
memory.assertValidPath = orig.assertValidPath;
|
|
296
|
+
|
|
297
|
+
// Stub the audit insert: async, records the row, optionally throws. Making it
|
|
298
|
+
// async + asserting the response waited for it proves the route AWAITS it.
|
|
299
|
+
db.insertAuditLog = async function stubAudit(row) {
|
|
300
|
+
// yield a turn so a non-awaited caller would race ahead of us
|
|
301
|
+
await new Promise((r) => setImmediate(r));
|
|
302
|
+
if (fixture.auditShouldThrow) throw new Error('audit db down');
|
|
303
|
+
fixture.auditCalls.push(row);
|
|
304
|
+
return { id: fixture.auditCalls.length, recorded_at: new Date().toISOString() };
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
// Build the REAL router AFTER the auth stubs are in place (middleware captured
|
|
308
|
+
// at build time). express is a CJS dep already in the tree.
|
|
309
|
+
const express = require('express');
|
|
310
|
+
const app = express();
|
|
311
|
+
app.use(express.json());
|
|
312
|
+
app.use('/api/gds', buildMemoryRouter());
|
|
313
|
+
|
|
314
|
+
const server = http.createServer(app);
|
|
315
|
+
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
316
|
+
const base = `http://127.0.0.1:${server.address().port}/api/gds`;
|
|
317
|
+
|
|
318
|
+
function reset() {
|
|
319
|
+
fixture.storeCalls = [];
|
|
320
|
+
fixture.auditCalls = [];
|
|
321
|
+
fixture.auditShouldThrow = false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
await t('(a) NON-Archon authenticated builder hitting the forensic route → 403 (rank gate)', async () => {
|
|
326
|
+
reset();
|
|
327
|
+
fixture.session = { id: 1, rank: 'xenos' };
|
|
328
|
+
const res = await fetch(`${base}/memory/builders/99/files`);
|
|
329
|
+
assert.equal(res.status, 403, 'a non-archon must be refused by the rank gate');
|
|
330
|
+
const body = await res.json();
|
|
331
|
+
assert.equal(body.error, 'rank_forbidden');
|
|
332
|
+
// The store must NEVER have been touched for builder 99.
|
|
333
|
+
assert.equal(fixture.storeCalls.length, 0, 'no store call may happen when the rank gate refuses');
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
await t('(a2) a metic is ALSO refused the forensic route (archon-only)', async () => {
|
|
337
|
+
reset();
|
|
338
|
+
fixture.session = { id: 5, rank: 'metic' };
|
|
339
|
+
const res = await fetch(`${base}/memory/builders/99/file?path=MEMORY.md`);
|
|
340
|
+
assert.equal(res.status, 403);
|
|
341
|
+
assert.equal(fixture.storeCalls.length, 0);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
await t('(b) GET /memory/files only ever lists the AUTHENTICATED builder', async () => {
|
|
345
|
+
reset();
|
|
346
|
+
fixture.session = { id: 42, rank: 'xenos' };
|
|
347
|
+
const res = await fetch(`${base}/memory/files`);
|
|
348
|
+
assert.equal(res.status, 200);
|
|
349
|
+
const body = await res.json();
|
|
350
|
+
assert.equal(body.builder_id, 42);
|
|
351
|
+
// The store was asked for builder 42 — the session id — and nothing else.
|
|
352
|
+
assert.equal(fixture.storeCalls.length, 1);
|
|
353
|
+
assert.equal(fixture.storeCalls[0].fn, 'listMemoryFiles');
|
|
354
|
+
assert.equal(fixture.storeCalls[0].builderId, 42, 'self endpoint must pass the SESSION id to the store');
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
await t('(b2) GET /memory/file only ever fetches the AUTHENTICATED builder (no owner param to even request another)', async () => {
|
|
358
|
+
reset();
|
|
359
|
+
fixture.session = { id: 42, rank: 'xenos' };
|
|
360
|
+
const res = await fetch(`${base}/memory/file?path=MEMORY.md`);
|
|
361
|
+
assert.equal(res.status, 200);
|
|
362
|
+
const body = await res.json();
|
|
363
|
+
assert.equal(body.builder_id, 42);
|
|
364
|
+
assert.equal(fixture.storeCalls.length, 1);
|
|
365
|
+
assert.equal(fixture.storeCalls[0].fn, 'fetchMemoryFile');
|
|
366
|
+
assert.equal(fixture.storeCalls[0].builderId, 42, 'self endpoint must pass the SESSION id, never a path-supplied owner');
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
await t('(c) an Archon hitting the forensic route → 200 AND an audit row was written (and awaited)', async () => {
|
|
370
|
+
reset();
|
|
371
|
+
fixture.session = { id: 7, rank: 'archon' };
|
|
372
|
+
const res = await fetch(`${base}/memory/builders/99/files`);
|
|
373
|
+
assert.equal(res.status, 200, 'archon is allowed through the rank gate');
|
|
374
|
+
const body = await res.json();
|
|
375
|
+
assert.equal(body.builder_id, 99);
|
|
376
|
+
assert.equal(body.forensic, true);
|
|
377
|
+
// The store was queried for the TARGET (99), the audit attributes it to the ARCHON (7).
|
|
378
|
+
assert.equal(fixture.storeCalls[0].builderId, 99, 'forensic read targets the path-supplied id');
|
|
379
|
+
assert.equal(fixture.auditCalls.length, 1, 'exactly one audit row must have been written');
|
|
380
|
+
assert.equal(fixture.auditCalls[0].builderId, 7, 'audit attributes the read to the archon');
|
|
381
|
+
assert.equal(fixture.auditCalls[0].responseStatus, 200, 'a successful list is audited as 200');
|
|
382
|
+
// If the route had NOT awaited the (async) audit insert, the 200 response
|
|
383
|
+
// would have raced ahead and auditCalls would be empty when we read it.
|
|
384
|
+
// It is populated → the route awaited it.
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
await t('(c2) forensic file read of a MISSING file → 404 AND audited as 404 (accurate status)', async () => {
|
|
388
|
+
reset();
|
|
389
|
+
fixture.session = { id: 7, rank: 'archon' };
|
|
390
|
+
const res = await fetch(`${base}/memory/builders/99/file?path=missing.md`);
|
|
391
|
+
assert.equal(res.status, 404);
|
|
392
|
+
assert.equal(fixture.auditCalls.length, 1, 'a 404 look is still audited (the Archon TRIED to read it)');
|
|
393
|
+
assert.equal(fixture.auditCalls[0].responseStatus, 404, 'the absent-file look must be audited as 404, not 200');
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
await t('(d) FAIL CLOSED: if the audit insert throws, the forensic read → 500 and does NOT leak the target data', async () => {
|
|
397
|
+
reset();
|
|
398
|
+
fixture.session = { id: 7, rank: 'archon' };
|
|
399
|
+
fixture.auditShouldThrow = true;
|
|
400
|
+
const res = await fetch(`${base}/memory/builders/99/file?path=MEMORY.md`);
|
|
401
|
+
assert.equal(res.status, 500, 'a failed audit must fail the read closed');
|
|
402
|
+
const body = await res.json();
|
|
403
|
+
assert.equal(body.error, 'audit_failed');
|
|
404
|
+
// Critically: the target's file content must NOT be in the response.
|
|
405
|
+
assert.ok(!('content' in body), 'the target builder content must NOT leak when the audit failed');
|
|
406
|
+
assert.equal(body.content, undefined);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
await t('(d2) FAIL CLOSED also applies to the forensic LIST endpoint', async () => {
|
|
410
|
+
reset();
|
|
411
|
+
fixture.session = { id: 7, rank: 'archon' };
|
|
412
|
+
fixture.auditShouldThrow = true;
|
|
413
|
+
const res = await fetch(`${base}/memory/builders/99/files`);
|
|
414
|
+
assert.equal(res.status, 500);
|
|
415
|
+
const body = await res.json();
|
|
416
|
+
assert.equal(body.error, 'audit_failed');
|
|
417
|
+
assert.ok(!('files' in body), 'the target builder file list must NOT leak when the audit failed');
|
|
418
|
+
});
|
|
419
|
+
} finally {
|
|
420
|
+
await new Promise((resolve) => server.close(resolve));
|
|
421
|
+
// restore originals so we don't poison any later test in the same process
|
|
422
|
+
auth.requireBuilder = orig.requireBuilder;
|
|
423
|
+
auth.requireRank = orig.requireRank;
|
|
424
|
+
memory.listMemoryFiles = orig.listMemoryFiles;
|
|
425
|
+
memory.fetchMemoryFile = orig.fetchMemoryFile;
|
|
426
|
+
db.insertAuditLog = orig.insertAuditLog;
|
|
427
|
+
memory.assertValidPath = orig.assertValidPath;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
console.log(`\n${passed} passed, ${failed} failed`);
|
|
431
|
+
process.exit(failed === 0 ? 0 : 1);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Tests for scripts/gds/memory-map.js — the memory-map guard + auto-tidy (task 1362).
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { test } from 'node:test';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
10
|
+
const mm = require(path.join(ROOT, 'scripts/gds/memory-map.js'));
|
|
11
|
+
|
|
12
|
+
// Build a synthetic MEMORY.md: `rules` feedback_* entries + `projects` project-NNN
|
|
13
|
+
// entries, each hook `hookLen` chars. mtime is injected by index so project-001 is
|
|
14
|
+
// oldest and project-<projects> is newest.
|
|
15
|
+
function mkMap({ rules = 3, projects = 45, hookLen = 400 } = {}) {
|
|
16
|
+
const lines = [];
|
|
17
|
+
for (let i = 1; i <= rules; i++) lines.push(`- [rule ${i}](feedback_rule_${i}.md) — ${'x'.repeat(hookLen)}`);
|
|
18
|
+
for (let i = 1; i <= projects; i++) {
|
|
19
|
+
const n = String(i).padStart(3, '0');
|
|
20
|
+
lines.push(`- [proj ${n}](project-${n}.md) — ${'x'.repeat(hookLen)}`);
|
|
21
|
+
}
|
|
22
|
+
return lines.join('\n') + '\n';
|
|
23
|
+
}
|
|
24
|
+
// Deterministic metadata: feedback_* → rule; project-NNN → project, mtime = NNN.
|
|
25
|
+
const metaOf = (file) => /^feedback_/.test(file)
|
|
26
|
+
? { type: 'feedback', mtime: 0 }
|
|
27
|
+
: { type: 'project', mtime: Number((file.match(/(\d+)/) || [0, 0])[1]) };
|
|
28
|
+
|
|
29
|
+
test('measure flags over-budget sizes, counts entries, and catches over-long lines', () => {
|
|
30
|
+
const small = '- [a](a.md) — short\n';
|
|
31
|
+
assert.equal(mm.measure(small).overWarn, false);
|
|
32
|
+
assert.equal(mm.measure(small).count, 1);
|
|
33
|
+
|
|
34
|
+
const big = mkMap({ rules: 3, projects: 45, hookLen: 400 }); // ~21KB
|
|
35
|
+
const m = mm.measure(big);
|
|
36
|
+
assert.equal(m.overWarn, true);
|
|
37
|
+
assert.equal(m.overTidy, true, 'a ~21KB map must be over the tidy threshold');
|
|
38
|
+
assert.equal(m.count, 48);
|
|
39
|
+
|
|
40
|
+
const longLine = `- [x](x.md) — ${'y'.repeat(mm.MAX_LINE_CHARS + 10)}\n`;
|
|
41
|
+
assert.equal(mm.measure(longLine).longLines.length, 1, 'a >MAX_LINE_CHARS entry is flagged');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('auto-tidy keeps every rule + the newest non-rule entries, demotes the oldest', () => {
|
|
45
|
+
const text = mkMap({ rules: 3, projects: 45, hookLen: 400 });
|
|
46
|
+
const plan = mm.planCompaction(text, { metaOf, keepNewest: 25, targetBytes: mm.TARGET_BYTES });
|
|
47
|
+
|
|
48
|
+
assert.equal(plan.willChange, true, 'an over-target map must compact');
|
|
49
|
+
// No standing rule is ever demoted.
|
|
50
|
+
assert.ok(!plan.demote.some((d) => /^feedback_/.test(d.file)), 'rules must never be demoted');
|
|
51
|
+
assert.ok(plan.newText.includes('feedback_rule_1.md'), 'rule lines stay in the map');
|
|
52
|
+
// Oldest project demoted; newest kept.
|
|
53
|
+
assert.ok(plan.demote.some((d) => d.file === 'project-001.md'), 'the oldest non-rule entry is demoted');
|
|
54
|
+
assert.ok(!plan.demote.some((d) => d.file === 'project-045.md'), 'the newest non-rule entry is kept');
|
|
55
|
+
assert.ok(!plan.newText.includes('project-001.md'), 'a demoted entry leaves the map');
|
|
56
|
+
// Result is back under target.
|
|
57
|
+
assert.ok(mm.measure(plan.newText).bytes <= mm.TARGET_BYTES, 'compaction lands under target');
|
|
58
|
+
assert.equal(mm.measure(plan.newText).overTidy, false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('rules are never demoted even when the map is all rules and over target', () => {
|
|
62
|
+
const allRules = mkMap({ rules: 60, projects: 0, hookLen: 400 }); // big, but every entry is a rule
|
|
63
|
+
assert.equal(mm.measure(allRules).overTidy, true);
|
|
64
|
+
const plan = mm.planCompaction(allRules, { metaOf, keepNewest: 25, targetBytes: mm.TARGET_BYTES });
|
|
65
|
+
assert.equal(plan.demote.length, 0, 'a map of only standing rules demotes nothing');
|
|
66
|
+
assert.equal(plan.willChange, false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('a pinned entry stays on the home screen even when it is the oldest', () => {
|
|
70
|
+
const text = mkMap({ rules: 0, projects: 45, hookLen: 400 });
|
|
71
|
+
// pin the OLDEST project (project-001) — it would otherwise be demoted first.
|
|
72
|
+
const metaPinned = (file) => file === 'project-001.md'
|
|
73
|
+
? { type: 'project', mtime: 1, pin: true }
|
|
74
|
+
: metaOf(file);
|
|
75
|
+
const plan = mm.planCompaction(text, { metaOf: metaPinned, keepNewest: 25, targetBytes: mm.TARGET_BYTES });
|
|
76
|
+
assert.ok(plan.willChange, 'an over-target map still compacts');
|
|
77
|
+
assert.ok(!plan.demote.some((d) => d.file === 'project-001.md'), 'a pinned entry is never demoted');
|
|
78
|
+
assert.ok(plan.newText.includes('project-001.md'), 'the pinned entry stays in the map');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('demotion only removes lines — it never returns or touches a topic file', () => {
|
|
82
|
+
// planCompaction is pure on (text, meta); it produces newText, never filesystem ops.
|
|
83
|
+
const text = mkMap({ rules: 2, projects: 40, hookLen: 400 });
|
|
84
|
+
const plan = mm.planCompaction(text, { metaOf, keepNewest: 10, targetBytes: mm.TARGET_BYTES });
|
|
85
|
+
// every demoted file is simply absent from newText; nothing claims to delete it.
|
|
86
|
+
for (const d of plan.demote) assert.ok(!plan.newText.includes(`(${d.file})`), `${d.file} line removed`);
|
|
87
|
+
// kept + demoted accounts for all non-rule entries (none silently lost).
|
|
88
|
+
const keptEntries = mm.entries(plan.newText).length;
|
|
89
|
+
assert.equal(keptEntries + plan.demote.length, mm.entries(text).length, 'no entry vanishes unaccounted-for');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('applyCompaction is parallel-safe: it skips the write if the file changed since read', () => {
|
|
93
|
+
const expected = '- [a](a.md) — one\n';
|
|
94
|
+
const next = '- [a](a.md) — two\n';
|
|
95
|
+
let written = null;
|
|
96
|
+
const deps = { readFile: () => expected, writeFile: (_p, c) => { written = c; } };
|
|
97
|
+
assert.deepEqual(mm.applyCompaction('/x/MEMORY.md', expected, next, deps), { written: true });
|
|
98
|
+
assert.equal(written, next);
|
|
99
|
+
|
|
100
|
+
// Now a concurrent session changed the file out from under us → do NOT clobber.
|
|
101
|
+
written = null;
|
|
102
|
+
const deps2 = { readFile: () => 'something a parallel session wrote\n', writeFile: (_p, c) => { written = c; } };
|
|
103
|
+
const r = mm.applyCompaction('/x/MEMORY.md', expected, next, deps2);
|
|
104
|
+
assert.equal(r.written, false);
|
|
105
|
+
assert.equal(r.reason, 'changed-since-read');
|
|
106
|
+
assert.equal(written, null, 'must not write when the file changed since read');
|
|
107
|
+
});
|