@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,187 @@
|
|
|
1
|
+
// tests/goal_abuse_matrix.mjs — the goal wall's ABUSE MATRIX (BV1.R61, ADR 0086 §7).
|
|
2
|
+
//
|
|
3
|
+
// "How the boundary holds," made executable. The goal-scoped work hierarchy lets a
|
|
4
|
+
// lower-rank builder AUTHOR their own tasks under a goal (POST /goals/:id/tasks) —
|
|
5
|
+
// the maneuverability grant. This file is the single DOCUMENTED enumeration of every
|
|
6
|
+
// way a member might try to escape the wall and the exact server response that stops
|
|
7
|
+
// them. It runs the PURE decision core (no DB, no network): the ordered gate
|
|
8
|
+
// `authorizeGoalTaskCreate`, the auto-derived rank floor (ADR 0084), the route-source
|
|
9
|
+
// contract, and the route-rank PINS that keep the wall from drifting.
|
|
10
|
+
//
|
|
11
|
+
// THE MATRIX (attacker = a joined sub-Metic member unless a row says otherwise; goal
|
|
12
|
+
// open + version open unless the row overrides it):
|
|
13
|
+
//
|
|
14
|
+
// # attack vector → server response
|
|
15
|
+
// ── ───────────────────────────────────────────────── ──────────────────────
|
|
16
|
+
// A in-scope task, joined member (the grant) → ALLOW
|
|
17
|
+
// B out-of-scope touch (a module the goal doesn't own) → 403 out_of_scope
|
|
18
|
+
// C protected-core touch, in a protected-scope goal → 403 protected_path
|
|
19
|
+
// D author in a goal they never joined → 403 not_a_member
|
|
20
|
+
// E self-raised-rank escape (claim past the floor) → auto-floor to metic; unclaimable
|
|
21
|
+
// F a closed / archived goal → 409 goal_not_open
|
|
22
|
+
// G a shipped (closed) version → 409 version_closed
|
|
23
|
+
// H a scopeless goal (authorizes nothing) → 403 empty_scope
|
|
24
|
+
// I a goal whose modules resolve to no territory → 403 unknown_scope
|
|
25
|
+
// J scope bounds EVERYONE — even an Archon out-of-scope → 403 out_of_scope
|
|
26
|
+
// K self-join a protected-scope goal (membership wall) → blocked (Archon-mediated)
|
|
27
|
+
// N1 a goal that doesn't exist → 404 goal_not_found
|
|
28
|
+
// N2 a goal whose version vanished → 404 version_not_found
|
|
29
|
+
// O1 ordering: non-member + out-of-scope → not_a_member (no scope hint leaks)
|
|
30
|
+
// O2 ordering: a closed goal short-circuits first → goal_not_open
|
|
31
|
+
// P Metic+ on an in-scope protected path → ALLOW (rank clears the floor)
|
|
32
|
+
//
|
|
33
|
+
// Ordering is fail-closed (a missing/ambiguous input denies), and membership is
|
|
34
|
+
// checked BEFORE scope so a non-member never learns the scope (O1 vs B). Per-branch
|
|
35
|
+
// request-validation schemas live in tests/goal_routes.mjs; the pure two-wall
|
|
36
|
+
// internals in tests/goal_scope_check.mjs; the pin-DRIFT catch-violation in
|
|
37
|
+
// tests/module_route_rank.mjs. This file is the consolidated threat model that ties
|
|
38
|
+
// them together.
|
|
39
|
+
//
|
|
40
|
+
// Run: node tests/goal_abuse_matrix.mjs
|
|
41
|
+
|
|
42
|
+
import { strict as assert } from 'node:assert';
|
|
43
|
+
import { createRequire } from 'node:module';
|
|
44
|
+
import { readFileSync } from 'node:fs';
|
|
45
|
+
import { makeRunner } from './helpers.mjs';
|
|
46
|
+
|
|
47
|
+
process.env.NODE_ENV = 'test';
|
|
48
|
+
|
|
49
|
+
const require = createRequire(import.meta.url);
|
|
50
|
+
const buildGoalsRouter = require('../modules/lifecycle/routes/goals.js');
|
|
51
|
+
// The pure, exported, ordered create gate — the one place the decision is made.
|
|
52
|
+
const { authorizeGoalTaskCreate } = buildGoalsRouter;
|
|
53
|
+
const { deriveRequiredRank, highestRank } = require('../modules/lifecycle/db.js');
|
|
54
|
+
const { MODULE_EXPECTED_RANKS, checkRouteRanks } = require('../src/bongos/route-rank-check.js');
|
|
55
|
+
const { scopeIncludesProtected, protectedModules, isModuleKey } = require('../src/bongos/module-scope-map.js');
|
|
56
|
+
|
|
57
|
+
const goalsSrc = readFileSync(new URL('../modules/lifecycle/routes/goals.js', import.meta.url), 'utf8');
|
|
58
|
+
const { test, summary } = makeRunner();
|
|
59
|
+
|
|
60
|
+
// ---- fixtures: grounded in real module-map facts (anchored below) ----
|
|
61
|
+
// `ideas` is a NON-protected module (globs modules/ideas/…); `lifecycle` is PROTECTED
|
|
62
|
+
// (its globs include migrations/). Verified against the live module-scope-map +
|
|
63
|
+
// goal-scope-check in the session log.
|
|
64
|
+
const V_OPEN = { id: 'BONGOS-V1', status: 'planning' };
|
|
65
|
+
const V_SHIPPED = { id: 'V1', status: 'shipped' };
|
|
66
|
+
const G_IDEAS = { id: 5, version_id: 'BONGOS-V1', status: 'open', scope_modules: ['ideas'] };
|
|
67
|
+
const G_LIFECYCLE = { id: 6, version_id: 'BONGOS-V1', status: 'open', scope_modules: ['lifecycle'] };
|
|
68
|
+
const G_ARCHIVED = { id: 7, version_id: 'BONGOS-V1', status: 'archived', scope_modules: ['ideas'] };
|
|
69
|
+
const G_EMPTY = { id: 8, version_id: 'BONGOS-V1', status: 'open', scope_modules: [] };
|
|
70
|
+
const G_UNKNOWN = { id: 9, version_id: 'BONGOS-V1', status: 'open', scope_modules: ['not-a-real-module'] };
|
|
71
|
+
const IN_SCOPE = ['modules/ideas/routes/inbox.js'];
|
|
72
|
+
const OUT_SCOPE = ['src/bongos/db.js'];
|
|
73
|
+
const PROTECTED = ['migrations/999_goal_authored.sql'];
|
|
74
|
+
|
|
75
|
+
await test('fixtures: ideas is a real UNprotected module; lifecycle is protected (anchors the matrix)', () => {
|
|
76
|
+
assert.ok(isModuleKey('ideas') && isModuleKey('lifecycle'), 'both fixture modules must be real');
|
|
77
|
+
assert.ok(!protectedModules().includes('ideas'), 'ideas must be unprotected for the maneuverability vectors');
|
|
78
|
+
assert.ok(protectedModules().includes('lifecycle'), 'lifecycle owns migrations/ + routes/ so it is protected');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// ---- the matrix: run every vector through the ordered gate ----
|
|
82
|
+
const MATRIX = [
|
|
83
|
+
{ id: 'A', desc: 'in-scope task, joined member — the maneuverability grant', goal: G_IDEAS, version: V_OPEN, isMember: true, rank: 'xenos', touches: IN_SCOPE, expect: { ok: true } },
|
|
84
|
+
{ id: 'B', desc: 'out-of-scope touch (a module the goal does not own)', goal: G_IDEAS, version: V_OPEN, isMember: true, rank: 'xenos', touches: OUT_SCOPE, expect: { status: 403, error: 'out_of_scope' } },
|
|
85
|
+
{ id: 'C', desc: 'sub-Metic authoring into the protected core (protected-scope goal)', goal: G_LIFECYCLE, version: V_OPEN, isMember: true, rank: 'xenos', touches: PROTECTED, expect: { status: 403, error: 'protected_path' } },
|
|
86
|
+
{ id: 'D', desc: 'author in a goal they never joined', goal: G_IDEAS, version: V_OPEN, isMember: false, rank: 'xenos', touches: IN_SCOPE, expect: { status: 403, error: 'not_a_member' } },
|
|
87
|
+
{ id: 'F', desc: 'a closed / archived goal takes no new work', goal: G_ARCHIVED, version: V_OPEN, isMember: true, rank: 'metic', touches: IN_SCOPE, expect: { status: 409, error: 'goal_not_open' } },
|
|
88
|
+
{ id: 'G', desc: 'a shipped (closed) version takes no new work', goal: { ...G_IDEAS, version_id: 'V1' }, version: V_SHIPPED, isMember: true, rank: 'metic', touches: IN_SCOPE, expect: { status: 409, error: 'version_closed' } },
|
|
89
|
+
{ id: 'H', desc: 'a scopeless goal authorizes nothing', goal: G_EMPTY, version: V_OPEN, isMember: true, rank: 'metic', touches: IN_SCOPE, expect: { status: 403, error: 'empty_scope' } },
|
|
90
|
+
{ id: 'I', desc: 'a goal whose modules resolve to no territory fails closed', goal: G_UNKNOWN, version: V_OPEN, isMember: true, rank: 'metic', touches: IN_SCOPE, expect: { status: 403, error: 'unknown_scope' } },
|
|
91
|
+
{ id: 'J', desc: 'scope bounds EVERYONE — even an Archon is blocked out-of-scope', goal: G_IDEAS, version: V_OPEN, isMember: true, rank: 'archon', touches: OUT_SCOPE, expect: { status: 403, error: 'out_of_scope' } },
|
|
92
|
+
{ id: 'N1', desc: 'a goal that does not exist', goal: null, version: null, isMember: false, rank: 'xenos', touches: IN_SCOPE, expect: { status: 404, error: 'goal_not_found' } },
|
|
93
|
+
{ id: 'N2', desc: 'a goal whose version vanished', goal: G_IDEAS, version: null, isMember: true, rank: 'xenos', touches: IN_SCOPE, expect: { status: 404, error: 'version_not_found' } },
|
|
94
|
+
{ id: 'O1', desc: 'ordering: non-member + out-of-scope → not_a_member (no scope hint leaks)', goal: G_IDEAS, version: V_OPEN, isMember: false, rank: 'xenos', touches: OUT_SCOPE, expect: { status: 403, error: 'not_a_member' } },
|
|
95
|
+
{ id: 'O2', desc: 'ordering: a closed goal short-circuits before membership/scope', goal: G_ARCHIVED, version: V_SHIPPED, isMember: false, rank: 'xenos', touches: OUT_SCOPE, expect: { status: 409, error: 'goal_not_open' } },
|
|
96
|
+
{ id: 'P', desc: 'Metic+ clears the protected floor on an in-scope protected path', goal: G_LIFECYCLE, version: V_OPEN, isMember: true, rank: 'metic', touches: PROTECTED, expect: { ok: true } },
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
for (const row of MATRIX) {
|
|
100
|
+
await test(`[${row.id}] ${row.desc}`, () => {
|
|
101
|
+
const d = authorizeGoalTaskCreate({
|
|
102
|
+
goal: row.goal, version: row.version, isMember: row.isMember, builderRank: row.rank, touches: row.touches,
|
|
103
|
+
});
|
|
104
|
+
if (row.expect.ok) {
|
|
105
|
+
assert.equal(d.ok, true, `[${row.id}] expected ALLOW, got ${JSON.stringify(d)}`);
|
|
106
|
+
} else {
|
|
107
|
+
assert.equal(d.ok, false, `[${row.id}] expected a rejection`);
|
|
108
|
+
assert.equal(d.status, row.expect.status, `[${row.id}] HTTP status`);
|
|
109
|
+
assert.equal(d.body.error, row.expect.error, `[${row.id}] error code`);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await test('[B+] a mixed touch list rejects and names ONLY the out-of-scope offender', () => {
|
|
115
|
+
const d = authorizeGoalTaskCreate({
|
|
116
|
+
goal: G_IDEAS, version: V_OPEN, isMember: true, builderRank: 'xenos',
|
|
117
|
+
touches: ['modules/ideas/a.js', 'src/bongos/db.js', 'modules/ideas/b.js'],
|
|
118
|
+
});
|
|
119
|
+
assert.equal(d.body.error, 'out_of_scope');
|
|
120
|
+
assert.deepEqual(d.body.details.out_of_scope, ['src/bongos/db.js'], 'only the true out-of-scope path is named');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ---- [E] no self-raised-rank escape (the floor is auto-derived, ADR 0084) ----
|
|
124
|
+
// The gate never trusts a client rank; requires_rank is DERIVED and can only be
|
|
125
|
+
// RAISED. So a sub-Metic cannot mint a task they could then claim past the boundary:
|
|
126
|
+
// an in-scope non-protected task derives to their own floor (claimable), a
|
|
127
|
+
// protected/sensitive one auto-floors to metic (NOT claimable by them), and an
|
|
128
|
+
// explicit request only raises. The route never forwards a client rank or version.
|
|
129
|
+
await test('[E] floor: an in-scope non-protected task derives to xenos (author can claim their own work)', () => {
|
|
130
|
+
assert.equal(deriveRequiredRank(IN_SCOPE, false), 'xenos');
|
|
131
|
+
});
|
|
132
|
+
await test('[E] floor: a protected-path task auto-floors to metic — a sub-Metic author cannot claim it', () => {
|
|
133
|
+
assert.equal(deriveRequiredRank(PROTECTED, false), 'metic');
|
|
134
|
+
});
|
|
135
|
+
await test('[E] floor: an explicit requested rank can only RAISE, never drop below the derived floor', () => {
|
|
136
|
+
assert.equal(highestRank('metic', 'xenos'), 'metic'); // requesting xenos cannot escape a metic floor
|
|
137
|
+
assert.equal(highestRank('xenos', 'archon'), 'archon'); // an explicit value only raises
|
|
138
|
+
});
|
|
139
|
+
await test('[E] route contract: goals.js never forwards a client requiresRank into createTask', () => {
|
|
140
|
+
assert.ok(!/requiresRank\s*:/.test(goalsSrc), 'goals.js must not pass requiresRank to createTask — the floor auto-derives (ADR 0084)');
|
|
141
|
+
});
|
|
142
|
+
await test('[E] route contract: the authored task version is DERIVED from the goal, not client input', () => {
|
|
143
|
+
assert.ok(/versionId:\s*goal\.version_id/.test(goalsSrc), 'createTask must use goal.version_id — a member cannot author cross-version');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ---- [K] the membership wall (distinct from task authoring) ----
|
|
147
|
+
// A sub-Metic cannot SELF-JOIN a protected-scope goal: POST /goals/:id/join fires
|
|
148
|
+
// `scopeIncludesProtected(scope) && rank !== 'archon' → 403`, and admission runs
|
|
149
|
+
// through POST /goals/:id/members — widened from Archon-only to the goal's own
|
|
150
|
+
// owner/manager (task 1762), but still Archon-only IN-HANDLER when the goal's
|
|
151
|
+
// scope is protected (archon_required_for_protected_scope). We assert the pure
|
|
152
|
+
// predicate the route branches on (the single protected roster, module-scope-map).
|
|
153
|
+
await test('[K] a protected-scope goal is recognized as protected; a normal goal is open-join', () => {
|
|
154
|
+
assert.equal(scopeIncludesProtected(G_LIFECYCLE.scope_modules), true, 'lifecycle owns migrations/ → protected → Archon-mediated join');
|
|
155
|
+
assert.equal(scopeIncludesProtected(G_IDEAS.scope_modules), false, 'ideas is a normal, open-join module');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// ---- the pins that make a rank drift on a goal route a red build (BV1.R61) ----
|
|
159
|
+
// The wall is only drift-proof if the goal routes are PINNED and the LIVE routes
|
|
160
|
+
// classify to those pins. The catch-violation (a downgrade → blocker) is proven in
|
|
161
|
+
// tests/module_route_rank.mjs; here we assert the pins exist and match reality.
|
|
162
|
+
await test('[pin] every goal route is pinned in MODULE_EXPECTED_RANKS.lifecycle', () => {
|
|
163
|
+
const pins = MODULE_EXPECTED_RANKS.lifecycle || {};
|
|
164
|
+
assert.equal(pins['POST /goals/:id/tasks'], 'any-builder', 'the enforcement keystone must be pinned');
|
|
165
|
+
assert.equal(pins['POST /goals/:id/join'], 'any-builder');
|
|
166
|
+
assert.equal(pins['POST /goals/:id/leave'], 'any-builder');
|
|
167
|
+
assert.equal(pins['POST /goals/:id/archive'], 'metic+archon');
|
|
168
|
+
assert.equal(pins['POST /goals/:id/reopen'], 'metic+archon');
|
|
169
|
+
assert.equal(pins['POST /goals'], 'metic+archon');
|
|
170
|
+
assert.equal(pins['POST /goals/:id/members'], 'any-builder', 'task 1762: widened to owner|lead|archon, in-handler gate');
|
|
171
|
+
assert.equal(pins['POST /goals/:id/scope'], 'metic+archon');
|
|
172
|
+
assert.equal(pins['POST /goals/reorder'], 'archon', 'priority is an owner decision, never self-granted');
|
|
173
|
+
});
|
|
174
|
+
await test('[pin] LIVE: every pinned goal route classifies to its expected rank (a drift would mismatch)', () => {
|
|
175
|
+
const res = checkRouteRanks();
|
|
176
|
+
const byKey = new Map();
|
|
177
|
+
for (const r of res.routes) if (!byKey.has(r.key)) byKey.set(r.key, r);
|
|
178
|
+
const pins = MODULE_EXPECTED_RANKS.lifecycle || {};
|
|
179
|
+
for (const [key, expected] of Object.entries(pins)) {
|
|
180
|
+
if (!key.includes('/goals')) continue; // this suite owns the goal-route pins
|
|
181
|
+
const r = byKey.get(key);
|
|
182
|
+
assert.ok(r, `pinned goal route ${key} must exist in the live route set`);
|
|
183
|
+
assert.equal(r.rank, expected, `goal route ${key} must classify as '${expected}', got '${r.rank}'`);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
summary();
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// tests/goal_achievement.mjs
|
|
2
|
+
//
|
|
3
|
+
// Unit guard for the criterion-confirm → goal-auto-achieve flow (ADR 0086 §6,
|
|
4
|
+
// BV1.R63 / task 1517):
|
|
5
|
+
// - db.achieveGoalIfComplete — flips a goal open→achieved when its LAST
|
|
6
|
+
// criterion is confirmed; conservative on every other shape.
|
|
7
|
+
// - done-when.listPendingReviewCriteria — the review-queue read (the auto-flag
|
|
8
|
+
// SQL: ≥1 linked task, all shipped, not yet satisfied).
|
|
9
|
+
//
|
|
10
|
+
// DB-free, same posture as goal_tier.mjs: each helper honours the #538 deps.pool
|
|
11
|
+
// injection seam (ONLY when NODE_ENV==='test'), so a SQL-aware fake client drives
|
|
12
|
+
// it with no live Postgres. We assert the statement shapes, the bound params, and
|
|
13
|
+
// — crucially for the auto-achieve — WHICH statements run on each goal shape (an
|
|
14
|
+
// UPDATE must fire only when the goal actually completes).
|
|
15
|
+
//
|
|
16
|
+
// The pure auto-flag derivation (buildCriterionProgress → pending_review) is
|
|
17
|
+
// covered alongside its siblings in criterion_progress.mjs.
|
|
18
|
+
//
|
|
19
|
+
// Run: node tests/goal_achievement.mjs
|
|
20
|
+
|
|
21
|
+
import { strict as assert } from 'node:assert';
|
|
22
|
+
import { createRequire } from 'node:module';
|
|
23
|
+
import { makeRunner, makeSqlAwareClient } from './helpers.mjs';
|
|
24
|
+
|
|
25
|
+
process.env.NODE_ENV = 'test';
|
|
26
|
+
|
|
27
|
+
const require = createRequire(import.meta.url);
|
|
28
|
+
const { achieveGoalIfComplete } = require('../modules/lifecycle/db.js');
|
|
29
|
+
const { listPendingReviewCriteria } = require('../modules/lifecycle/done-when.js');
|
|
30
|
+
|
|
31
|
+
const { test, summary } = makeRunner();
|
|
32
|
+
|
|
33
|
+
function fakePool(handler) {
|
|
34
|
+
const c = makeSqlAwareClient(handler);
|
|
35
|
+
return { query: (sql, params) => c._client.query(sql, params), connect: c.connect, queries: c._client.queries };
|
|
36
|
+
}
|
|
37
|
+
const find = (queries, re) => queries.find((q) => re.test(String(q.sql).replace(/\s+/g, ' ')));
|
|
38
|
+
|
|
39
|
+
const GOAL = (over = {}) => ({
|
|
40
|
+
id: 7, version_id: 'BONGOS-V1', title: 'Goal hierarchy', description: null,
|
|
41
|
+
scope_modules: [], status: 'open', created_by: 42, succeeded_by_goal_id: null,
|
|
42
|
+
created_at: 'T0', updated_at: 'T0', ...over,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// A handler factory: respond to getGoal / the criteria count / setGoalStatus.
|
|
46
|
+
// `counts` = { total, unsatisfied }; `goal` = the row getGoal returns (or null).
|
|
47
|
+
function goalHandler({ goal, counts }) {
|
|
48
|
+
return (s) => {
|
|
49
|
+
if (/^SELECT .*FROM goals WHERE id = \$1/.test(s)) return { rows: goal ? [goal] : [] };
|
|
50
|
+
if (/FROM done_when_criteria WHERE goal_id = \$1/.test(s)) return { rows: [counts] };
|
|
51
|
+
if (/^UPDATE goals/.test(s)) return { rows: [{ ...goal, status: 'achieved', updated_at: 'T1' }] };
|
|
52
|
+
return null;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// =========================================================================
|
|
57
|
+
// achieveGoalIfComplete — the happy path: last criterion confirmed → achieved
|
|
58
|
+
// =========================================================================
|
|
59
|
+
await test('achieveGoalIfComplete: open goal, all criteria satisfied → flips to achieved', async () => {
|
|
60
|
+
const pool = fakePool(goalHandler({ goal: GOAL(), counts: { total: 3, unsatisfied: 0 } }));
|
|
61
|
+
const out = await achieveGoalIfComplete({ goalId: 7 }, { pool });
|
|
62
|
+
assert.equal(out.achieved, true, 'a fully-satisfied open goal auto-achieves');
|
|
63
|
+
assert.equal(out.goal.status, 'achieved', 'returns the updated row');
|
|
64
|
+
const upd = find(pool.queries, /^UPDATE goals/);
|
|
65
|
+
assert.ok(upd, 'the status flip must be persisted');
|
|
66
|
+
assert.deepEqual(upd.params, [7, 'achieved'], 'UPDATE binds (goalId, "achieved")');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// =========================================================================
|
|
70
|
+
// achieveGoalIfComplete — conservative on every non-completing shape
|
|
71
|
+
// =========================================================================
|
|
72
|
+
await test('achieveGoalIfComplete: an unsatisfied criterion remains → does NOT flip, no UPDATE', async () => {
|
|
73
|
+
const pool = fakePool(goalHandler({ goal: GOAL(), counts: { total: 3, unsatisfied: 1 } }));
|
|
74
|
+
const out = await achieveGoalIfComplete({ goalId: 7 }, { pool });
|
|
75
|
+
assert.equal(out.achieved, false, 'one criterion still open → goal stays open');
|
|
76
|
+
assert.equal(out.goal.status, 'open');
|
|
77
|
+
assert.equal(find(pool.queries, /^UPDATE goals/), undefined, 'must NOT touch the goal status');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await test('achieveGoalIfComplete: a goal with ZERO criteria never auto-achieves', async () => {
|
|
81
|
+
const pool = fakePool(goalHandler({ goal: GOAL(), counts: { total: 0, unsatisfied: 0 } }));
|
|
82
|
+
const out = await achieveGoalIfComplete({ goalId: 7 }, { pool });
|
|
83
|
+
assert.equal(out.achieved, false, 'an empty goal has nothing to complete it');
|
|
84
|
+
assert.equal(find(pool.queries, /^UPDATE goals/), undefined);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await test('achieveGoalIfComplete: an ALREADY-achieved goal is left untouched (no churn)', async () => {
|
|
88
|
+
const pool = fakePool(goalHandler({ goal: GOAL({ status: 'achieved' }), counts: { total: 3, unsatisfied: 0 } }));
|
|
89
|
+
const out = await achieveGoalIfComplete({ goalId: 7 }, { pool });
|
|
90
|
+
assert.equal(out.achieved, false, 're-confirming a criterion under an achieved goal must not re-flip');
|
|
91
|
+
assert.equal(out.goal.status, 'achieved', 'returns the existing row unchanged');
|
|
92
|
+
// Short-circuits right after getGoal — it must NOT even count the criteria.
|
|
93
|
+
assert.equal(find(pool.queries, /FROM done_when_criteria/), undefined, 'a non-open goal short-circuits before the count');
|
|
94
|
+
assert.equal(find(pool.queries, /^UPDATE goals/), undefined);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
await test('achieveGoalIfComplete: an archived goal is left untouched', async () => {
|
|
98
|
+
const pool = fakePool(goalHandler({ goal: GOAL({ status: 'archived' }), counts: { total: 1, unsatisfied: 0 } }));
|
|
99
|
+
const out = await achieveGoalIfComplete({ goalId: 7 }, { pool });
|
|
100
|
+
assert.equal(out.achieved, false);
|
|
101
|
+
assert.equal(out.goal.status, 'archived');
|
|
102
|
+
assert.equal(find(pool.queries, /^UPDATE goals/), undefined);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
await test('achieveGoalIfComplete: unknown goal id → { achieved:false, goal:null }', async () => {
|
|
106
|
+
const pool = fakePool(goalHandler({ goal: null, counts: { total: 0, unsatisfied: 0 } }));
|
|
107
|
+
const out = await achieveGoalIfComplete({ goalId: 999 }, { pool });
|
|
108
|
+
assert.deepEqual(out, { achieved: false, goal: null });
|
|
109
|
+
assert.equal(find(pool.queries, /^UPDATE goals/), undefined);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// =========================================================================
|
|
113
|
+
// listPendingReviewCriteria — the review-queue read
|
|
114
|
+
// =========================================================================
|
|
115
|
+
const PENDING_ROW = {
|
|
116
|
+
id: 10, version_id: 'BONGOS-V1', criterion_id: 'c-one', criterion_md: 'md',
|
|
117
|
+
goal_id: 7, sort_order: 1, task_total: 2, goal_title: 'Goal hierarchy',
|
|
118
|
+
goal_status: 'open', is_last_in_goal: true,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
await test('listPendingReviewCriteria: returns the queue rows; no version → param [null]', async () => {
|
|
122
|
+
const pool = fakePool((s) => (/WITH pending AS/.test(s) ? { rows: [PENDING_ROW] } : null));
|
|
123
|
+
const out = await listPendingReviewCriteria({}, { pool });
|
|
124
|
+
assert.deepEqual(out, [PENDING_ROW]);
|
|
125
|
+
const q = find(pool.queries, /WITH pending AS/);
|
|
126
|
+
assert.deepEqual(q.params, [null], 'an unscoped call binds versionId=null (no filter)');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await test('listPendingReviewCriteria: versionId flows through to the bound param', async () => {
|
|
130
|
+
const pool = fakePool((s) => (/WITH pending AS/.test(s) ? { rows: [] } : null));
|
|
131
|
+
await listPendingReviewCriteria({ versionId: 'BONGOS-V1' }, { pool });
|
|
132
|
+
assert.deepEqual(find(pool.queries, /WITH pending AS/).params, ['BONGOS-V1']);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
await test('listPendingReviewCriteria: SQL encodes the auto-flag (all shipped) + is_last_in_goal', async () => {
|
|
136
|
+
const pool = fakePool((s) => (/WITH pending AS/.test(s) ? { rows: [] } : null));
|
|
137
|
+
await listPendingReviewCriteria({}, { pool });
|
|
138
|
+
const sql = String(find(pool.queries, /WITH pending AS/).sql).replace(/\s+/g, ' ');
|
|
139
|
+
// The derivation: satisfied=false AND every linked task shipped (zero non-shipped).
|
|
140
|
+
assert.match(sql, /c\.satisfied = false/, 'only unconfirmed criteria are pending');
|
|
141
|
+
assert.match(sql, /HAVING count\(\*\) FILTER \(WHERE t\.status <> 'shipped'\) = 0/, 'all linked tasks must be shipped');
|
|
142
|
+
assert.match(sql, /is_last_in_goal/, 'surfaces whether confirming completes the goal');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// -------------------------------------------------------------------------
|
|
146
|
+
summary();
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
// tests/goal_conflicts.mjs — server-side goal task-conflict detection +
|
|
2
|
+
// owner/manager resolution actions (task 1736).
|
|
3
|
+
//
|
|
4
|
+
// GET /goals/:id/conflicts computes overlaps among a goal's OPEN tasks (and
|
|
5
|
+
// against other open goals sharing scope_modules) from their DECLARED touches[]
|
|
6
|
+
// — glob-aware via the canonical path-match machinery — with claim-aware
|
|
7
|
+
// severity. POST /goals/:id/conflicts/resolve is the owner/manager toolset:
|
|
8
|
+
// sequence (ADR 0015 dependency edge), flag_release (nudge the claim holder),
|
|
9
|
+
// rescope (ADR 0049 advisory touches rewrite, planning-time only). This file
|
|
10
|
+
// pins, in the goal_routes pattern:
|
|
11
|
+
//
|
|
12
|
+
// 1. the PURE detector (goal-conflicts.js): overlap semantics (exact,
|
|
13
|
+
// dir-prefix, glob tail), open-task filtering, severity, cross-goal pairs,
|
|
14
|
+
// ordering — all DB-free;
|
|
15
|
+
// 2. the PURE management gate (authorizeGoalManagement) ordering;
|
|
16
|
+
// 3. the request-validation schema of the resolve route + the action wall;
|
|
17
|
+
// 4. the db helpers' SQL contracts via the SQL-aware fake pool —
|
|
18
|
+
// listActiveClaimsForTasks / requestClaimRelease / rescopeTaskTouches;
|
|
19
|
+
// 5. the route-rank PIN: resolve stays 'any-builder' (the wall is in-handler).
|
|
20
|
+
//
|
|
21
|
+
// Run: node tests/goal_conflicts.mjs
|
|
22
|
+
|
|
23
|
+
import { strict as assert } from 'node:assert';
|
|
24
|
+
import { createRequire } from 'node:module';
|
|
25
|
+
import { makeRunner, makeSqlAwareClient } from './helpers.mjs';
|
|
26
|
+
|
|
27
|
+
process.env.NODE_ENV = 'test';
|
|
28
|
+
|
|
29
|
+
const require = createRequire(import.meta.url);
|
|
30
|
+
const { validate, LIMITS } = require('../src/bongos/routes/_helpers.js');
|
|
31
|
+
const {
|
|
32
|
+
RESOLVED_STATUSES, isOpenWithTouches, normalizeEntry, overlappingPaths,
|
|
33
|
+
severityFor, computeGoalConflicts,
|
|
34
|
+
} = require('../modules/lifecycle/goal-conflicts.js');
|
|
35
|
+
const { authorizeGoalManagement, RESOLVE_ACTIONS } = require('../modules/lifecycle/routes/goals.js');
|
|
36
|
+
const { listActiveClaimsForTasks, requestClaimRelease, rescopeTaskTouches } = require('../modules/lifecycle/db.js');
|
|
37
|
+
const { MODULE_EXPECTED_RANKS } = require('../src/bongos/route-rank-check.js');
|
|
38
|
+
|
|
39
|
+
const { test, summary } = makeRunner();
|
|
40
|
+
|
|
41
|
+
function fakePool(handler) {
|
|
42
|
+
const c = makeSqlAwareClient(handler);
|
|
43
|
+
return { query: (sql, params) => c._client.query(sql, params), connect: c.connect, queries: c._client.queries };
|
|
44
|
+
}
|
|
45
|
+
const find = (queries, re) => queries.find((q) => re.test(String(q.sql).replace(/\s+/g, ' ')));
|
|
46
|
+
|
|
47
|
+
const task = (id, touches, status = 'ready', title = `t${id}`) => ({ id, title, status, touches });
|
|
48
|
+
|
|
49
|
+
// =========================================================================
|
|
50
|
+
// 1a. overlap semantics — the glob-aware pairwise matcher
|
|
51
|
+
// =========================================================================
|
|
52
|
+
|
|
53
|
+
await test('overlappingPaths: exact match, dir-prefix both directions, keeps the more specific side', () => {
|
|
54
|
+
assert.deepEqual(overlappingPaths(['src/bongos/db.js'], ['src/bongos/db.js']), ['src/bongos/db.js']);
|
|
55
|
+
assert.deepEqual(overlappingPaths(['src/bongos/'], ['src/bongos/db.js']), ['src/bongos/db.js'],
|
|
56
|
+
'dir entry covers the file → the file is reported');
|
|
57
|
+
assert.deepEqual(overlappingPaths(['src/bongos/db.js'], ['src/bongos']), ['src/bongos/db.js'],
|
|
58
|
+
'bare dir (no trailing slash) covers too — matchOne semantics');
|
|
59
|
+
assert.deepEqual(overlappingPaths(['a/b.js'], ['c/d.js']), [], 'disjoint paths do not overlap');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await test('overlappingPaths: glob tails trim to their directory prefix', () => {
|
|
63
|
+
assert.deepEqual(normalizeEntry('src/bongos/*'), 'src/bongos');
|
|
64
|
+
assert.deepEqual(normalizeEntry('modules/hall-ui/**'), 'modules/hall-ui');
|
|
65
|
+
assert.deepEqual(overlappingPaths(['src/bongos/*'], ['src/bongos/routes/goals.js']), ['src/bongos/routes/goals.js']);
|
|
66
|
+
assert.deepEqual(overlappingPaths(['modules/hall-ui/**'], ['modules/hall-ui/public/work.js']),
|
|
67
|
+
['modules/hall-ui/public/work.js']);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await test('overlappingPaths: sibling files under a shared dir do NOT overlap (prefix, not ancestor-merge)', () => {
|
|
71
|
+
assert.deepEqual(overlappingPaths(['src/bongos/a.js'], ['src/bongos/b.js']), [],
|
|
72
|
+
'two different files in one dir are parallel-safe — only a covering entry conflicts');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await test('overlappingPaths: dedupes and sorts; empty/junk entries skipped', () => {
|
|
76
|
+
const out = overlappingPaths(['src/', 'src/x.js', ''], ['src/x.js', 'src/y.js']);
|
|
77
|
+
assert.deepEqual(out, ['src/x.js', 'src/y.js']);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// =========================================================================
|
|
81
|
+
// 1b. the detector — filtering, severity, cross-goal, ordering
|
|
82
|
+
// =========================================================================
|
|
83
|
+
|
|
84
|
+
await test('open-task filter: resolved statuses and empty touches are excluded', () => {
|
|
85
|
+
for (const s of ['completed', 'confirmed', 'shipped', 'abandoned']) {
|
|
86
|
+
assert.ok(RESOLVED_STATUSES.has(s), `${s} is resolved`);
|
|
87
|
+
assert.equal(isOpenWithTouches(task(1, ['a.js'], s)), false);
|
|
88
|
+
}
|
|
89
|
+
assert.equal(isOpenWithTouches(task(1, [], 'ready')), false, 'no touches → nothing to intersect');
|
|
90
|
+
assert.equal(isOpenWithTouches(task(1, ['a.js'], 'active')), true);
|
|
91
|
+
assert.equal(isOpenWithTouches(task(1, ['a.js'], 'blocked')), true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
await test('severity: both claimed high, one claimed medium, none low', () => {
|
|
95
|
+
assert.equal(severityFor(true, true), 'high');
|
|
96
|
+
assert.equal(severityFor(true, false), 'medium');
|
|
97
|
+
assert.equal(severityFor(false, true), 'medium');
|
|
98
|
+
assert.equal(severityFor(false, false), 'low');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await test('detector: in-goal pair with overlap, claim-aware severity, client shape', () => {
|
|
102
|
+
const conflicts = computeGoalConflicts({
|
|
103
|
+
goalTasks: [
|
|
104
|
+
task(10, ['src/bongos/db.js'], 'active', 'alpha'),
|
|
105
|
+
task(11, ['src/bongos/'], 'ready', 'beta'),
|
|
106
|
+
task(12, ['docs/'], 'ready'), // disjoint — no pair
|
|
107
|
+
task(13, ['src/bongos/db.js'], 'shipped'), // resolved — excluded
|
|
108
|
+
],
|
|
109
|
+
activeClaimTaskIds: new Set(['10']),
|
|
110
|
+
});
|
|
111
|
+
assert.equal(conflicts.length, 1);
|
|
112
|
+
const c = conflicts[0];
|
|
113
|
+
assert.equal(c.task_a, 10);
|
|
114
|
+
assert.equal(c.task_b, 11);
|
|
115
|
+
assert.equal(c.title_a, 'alpha');
|
|
116
|
+
assert.deepEqual(c.overlapping_paths, ['src/bongos/db.js']);
|
|
117
|
+
assert.equal(c.severity, 'medium', 'one active claim → medium');
|
|
118
|
+
assert.equal(c.has_active_claims, true);
|
|
119
|
+
assert.equal(c.cross_goal, false);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await test('detector: cross-goal pairs are marked and carry the sibling goal', () => {
|
|
123
|
+
const conflicts = computeGoalConflicts({
|
|
124
|
+
goalTasks: [task(10, ['modules/lifecycle/db.js'], 'ready')],
|
|
125
|
+
activeClaimTaskIds: new Set(['77']),
|
|
126
|
+
crossGoal: [{
|
|
127
|
+
goal: { id: 5, title: 'sibling goal' },
|
|
128
|
+
tasks: [
|
|
129
|
+
task(77, ['modules/lifecycle/'], 'active'),
|
|
130
|
+
task(78, ['modules/lifecycle/db.js'], 'shipped'), // resolved — excluded
|
|
131
|
+
],
|
|
132
|
+
}],
|
|
133
|
+
});
|
|
134
|
+
assert.equal(conflicts.length, 1);
|
|
135
|
+
const c = conflicts[0];
|
|
136
|
+
assert.equal(c.cross_goal, true);
|
|
137
|
+
assert.equal(c.goal_b, 5);
|
|
138
|
+
assert.equal(c.goal_b_title, 'sibling goal');
|
|
139
|
+
assert.equal(c.task_b, 77);
|
|
140
|
+
assert.equal(c.severity, 'medium');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await test('detector: ordering — most severe first, then task ids', () => {
|
|
144
|
+
const conflicts = computeGoalConflicts({
|
|
145
|
+
goalTasks: [
|
|
146
|
+
task(1, ['x/'], 'active'), task(2, ['x/a.js'], 'active'), // high pair
|
|
147
|
+
task(3, ['y/'], 'ready'), task(4, ['y/b.js'], 'ready'), // low pair
|
|
148
|
+
],
|
|
149
|
+
activeClaimTaskIds: new Set(['1', '2']),
|
|
150
|
+
});
|
|
151
|
+
assert.deepEqual(conflicts.map((c) => c.severity), ['high', 'low']);
|
|
152
|
+
assert.deepEqual(conflicts.map((c) => [c.task_a, c.task_b]), [[1, 2], [3, 4]]);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await test('detector: no inputs → no conflicts (and never throws)', () => {
|
|
156
|
+
assert.deepEqual(computeGoalConflicts(), []);
|
|
157
|
+
assert.deepEqual(computeGoalConflicts({ goalTasks: [task(1, ['a.js'])] }), []);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// =========================================================================
|
|
161
|
+
// 2. authorizeGoalManagement — the shared prefix gate
|
|
162
|
+
// =========================================================================
|
|
163
|
+
|
|
164
|
+
const OPEN_GOAL = { id: 7, status: 'open', created_by: '1' };
|
|
165
|
+
const manager = (builderId) => ({ goal_id: 7, builder_id: builderId, role: 'lead' });
|
|
166
|
+
const member = (builderId) => ({ goal_id: 7, builder_id: builderId, role: 'member' });
|
|
167
|
+
|
|
168
|
+
await test('gate: owner / manager / archon pass; ordering goal_not_found → goal_not_open → not_owner_or_manager', () => {
|
|
169
|
+
for (const actor of [
|
|
170
|
+
{ actorId: 1, actorRank: 'metic', actorMembership: manager(1) }, // owner
|
|
171
|
+
{ actorId: 3, actorRank: 'metic', actorMembership: manager(3) }, // manager
|
|
172
|
+
{ actorId: 99, actorRank: 'archon', actorMembership: null }, // archon
|
|
173
|
+
]) {
|
|
174
|
+
assert.deepEqual(authorizeGoalManagement({ goal: OPEN_GOAL, ...actor }), { ok: true });
|
|
175
|
+
}
|
|
176
|
+
assert.equal(authorizeGoalManagement({ goal: null, actorId: 99, actorRank: 'archon', actorMembership: null }).body.error,
|
|
177
|
+
'goal_not_found');
|
|
178
|
+
assert.equal(authorizeGoalManagement({ goal: { id: 7, status: 'archived', created_by: '1' }, actorId: 1, actorRank: 'metic', actorMembership: manager(1) }).body.error,
|
|
179
|
+
'goal_not_open', 'closed goal short-circuits even for the owner');
|
|
180
|
+
const denied = authorizeGoalManagement({ goal: OPEN_GOAL, actorId: 2, actorRank: 'metic', actorMembership: member(2) });
|
|
181
|
+
assert.equal(denied.status, 403);
|
|
182
|
+
assert.equal(denied.body.error, 'not_owner_or_manager', 'a plain member cannot manage');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
await test('gate: owner authority is created_by, not the role row', () => {
|
|
186
|
+
const d = authorizeGoalManagement({ goal: OPEN_GOAL, actorId: 1, actorRank: 'metic', actorMembership: member(1) });
|
|
187
|
+
assert.deepEqual(d, { ok: true });
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// =========================================================================
|
|
191
|
+
// 3. resolve-route contracts — schema + action wall (in-handler checks)
|
|
192
|
+
// =========================================================================
|
|
193
|
+
|
|
194
|
+
const RESOLVE_SCHEMA = {
|
|
195
|
+
action: { required: true, type: 'string' },
|
|
196
|
+
note: { type: 'string', maxLength: LIMITS.NOTES },
|
|
197
|
+
touches: { type: 'array', maxItems: 500, itemsType: 'string' },
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
await test('resolve schema: action required; touches must be an array of strings', () => {
|
|
201
|
+
const r = validate({}, RESOLVE_SCHEMA);
|
|
202
|
+
assert.ok(r && r.details.some((d) => d.field === 'action' && d.reason === 'missing'));
|
|
203
|
+
const r2 = validate({ action: 'rescope', touches: 'src/x.js' }, RESOLVE_SCHEMA);
|
|
204
|
+
assert.ok(r2 && r2.details.some((d) => d.field === 'touches' && d.reason === 'wrong_type'));
|
|
205
|
+
assert.equal(validate({ action: 'flag_release', note: 'please wrap up' }, RESOLVE_SCHEMA), null);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await test('resolve actions: exactly sequence | flag_release | rescope', () => {
|
|
209
|
+
assert.deepEqual(RESOLVE_ACTIONS, ['sequence', 'flag_release', 'rescope']);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
await test('task-id coercion contract: int + numeric string pass, junk rejected (containment reads)', () => {
|
|
213
|
+
const ok = (x) => Number.isInteger(Number(x)) && Number(x) > 0;
|
|
214
|
+
assert.ok(ok(42) && ok('42'));
|
|
215
|
+
assert.ok(!ok('42; DROP') && !ok('') && !ok(undefined) && !ok(0));
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// =========================================================================
|
|
219
|
+
// 4. db helpers — SQL contracts via the SQL-aware fake pool
|
|
220
|
+
// =========================================================================
|
|
221
|
+
|
|
222
|
+
await test('listActiveClaimsForTasks: ANY($1) over ids, active = released_at IS NULL, [] on empty input', async () => {
|
|
223
|
+
const ROW = { claim_id: 9, task_id: 10, builder_id: 3, claimed_at: 'T0', github_login: 'x' };
|
|
224
|
+
const pool = fakePool((s) => (/FROM claims c/.test(s) ? { rows: [ROW] } : null));
|
|
225
|
+
const out = await listActiveClaimsForTasks([10, 11], { pool });
|
|
226
|
+
assert.deepEqual(out, [ROW]);
|
|
227
|
+
const sel = find(pool.queries, /FROM claims c/);
|
|
228
|
+
assert.deepEqual(sel.params, [[10, 11]]);
|
|
229
|
+
const sql = String(sel.sql).replace(/\s+/g, ' ');
|
|
230
|
+
assert.match(sql, /c\.task_id = ANY\(\$1::bigint\[\]\)/);
|
|
231
|
+
assert.match(sql, /c\.released_at IS NULL/, 'active-claim convention (no status column on claims)');
|
|
232
|
+
|
|
233
|
+
const empty = fakePool(() => ({ rows: [] }));
|
|
234
|
+
assert.deepEqual(await listActiveClaimsForTasks([], { pool: empty }), []);
|
|
235
|
+
assert.equal(empty.queries.length, 0, 'no query for an empty id list');
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
await test('requestClaimRelease: flags only the ACTIVE claim; null when none (route answers 409)', async () => {
|
|
239
|
+
const ROW = { claim_id: 9, task_id: 10, builder_id: 3, release_requested_by: 1, release_requested_at: 'T1', release_requested_note: 'n' };
|
|
240
|
+
const pool = fakePool((s) => (/UPDATE claims/.test(s) ? { rows: [ROW] } : null));
|
|
241
|
+
const out = await requestClaimRelease({ taskId: 10, requestedBy: 1, note: 'n' }, { pool });
|
|
242
|
+
assert.deepEqual(out, ROW);
|
|
243
|
+
const upd = find(pool.queries, /UPDATE claims/);
|
|
244
|
+
assert.deepEqual(upd.params, [10, 1, 'n']);
|
|
245
|
+
const sql = String(upd.sql).replace(/\s+/g, ' ');
|
|
246
|
+
assert.match(sql, /WHERE task_id = \$1 AND released_at IS NULL/, 'only an active claim can be flagged');
|
|
247
|
+
assert.match(sql, /RETURNING id AS claim_id/);
|
|
248
|
+
|
|
249
|
+
const none = fakePool(() => ({ rows: [] }));
|
|
250
|
+
assert.equal(await requestClaimRelease({ taskId: 10, requestedBy: 1 }, { pool: none }), null);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
await test('rescopeTaskTouches: dedupes/trims entries, bumps updated_at, null on unknown task', async () => {
|
|
254
|
+
const ROW = { id: 10, touches: ['a.js', 'b/'], requires_rank: 'xenos' };
|
|
255
|
+
const pool = fakePool((s) => (/UPDATE tasks SET touches/.test(s) ? { rows: [ROW] } : null));
|
|
256
|
+
const out = await rescopeTaskTouches({ taskId: 10, touches: [' a.js ', 'a.js', 'b/', ''] }, { pool });
|
|
257
|
+
assert.deepEqual(out, ROW);
|
|
258
|
+
const upd = find(pool.queries, /UPDATE tasks SET touches/);
|
|
259
|
+
assert.deepEqual(upd.params, [10, ['a.js', 'b/'], 'xenos'], 'trimmed, deduped, empties dropped; innocuous floor stays xenos');
|
|
260
|
+
assert.match(String(upd.sql), /updated_at = now\(\)/);
|
|
261
|
+
|
|
262
|
+
const none = fakePool(() => ({ rows: [] }));
|
|
263
|
+
assert.equal(await rescopeTaskTouches({ taskId: 999, touches: ['x'] }, { pool: none }), null);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await test('rescopeTaskTouches: the ADR 0084 floor re-derives and RATCHETS UP, never down', async () => {
|
|
267
|
+
// protected touches → the derived floor is metic even though the task sat at xenos
|
|
268
|
+
const pool = fakePool((s) => (/UPDATE tasks SET touches/.test(s) ? { rows: [{ id: 10 }] } : null));
|
|
269
|
+
await rescopeTaskTouches({ taskId: 10, touches: ['migrations/999_x.sql'], currentRequiresRank: 'xenos' }, { pool });
|
|
270
|
+
assert.equal(find(pool.queries, /UPDATE tasks SET touches/).params[2], 'metic',
|
|
271
|
+
'protected-path touches raise the floor (deriveRequiredRank via matchProtected)');
|
|
272
|
+
|
|
273
|
+
// innocuous touches on a task already floored at metic → floor is KEPT
|
|
274
|
+
const pool2 = fakePool((s) => (/UPDATE tasks SET touches/.test(s) ? { rows: [{ id: 10 }] } : null));
|
|
275
|
+
await rescopeTaskTouches({ taskId: 10, touches: ['docs/x.md'], currentRequiresRank: 'metic' }, { pool: pool2 });
|
|
276
|
+
assert.equal(find(pool2.queries, /UPDATE tasks SET touches/).params[2], 'metic',
|
|
277
|
+
'rescoping to innocuous paths never LOWERS an existing floor');
|
|
278
|
+
|
|
279
|
+
// security_sensitive flag alone floors at metic regardless of paths
|
|
280
|
+
const pool3 = fakePool((s) => (/UPDATE tasks SET touches/.test(s) ? { rows: [{ id: 10 }] } : null));
|
|
281
|
+
await rescopeTaskTouches({ taskId: 10, touches: ['docs/x.md'], securitySensitive: true }, { pool: pool3 });
|
|
282
|
+
assert.equal(find(pool3.queries, /UPDATE tasks SET touches/).params[2], 'metic');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
await test('rescope wall: the route runs goal-scope-check verbatim (source contract)', async () => {
|
|
286
|
+
// The create-time wall (R60) must also bound the rescope surface — otherwise
|
|
287
|
+
// touches could be rewritten out of the goal's territory after passing the
|
|
288
|
+
// wall once. Pin the route source: the rescope branch calls
|
|
289
|
+
// goalScopeCheck.check(...) before the write, exactly like the create gate.
|
|
290
|
+
const fs = await import('node:fs');
|
|
291
|
+
const src = fs.readFileSync(new URL('../modules/lifecycle/routes/goals.js', import.meta.url), 'utf8');
|
|
292
|
+
const rescopeBranch = src.split("action === 'rescope'")[1] || '';
|
|
293
|
+
assert.match(rescopeBranch, /goalScopeCheck\.check\(goal\.scope_modules,\s*touches,\s*req\.builder\.rank\)/,
|
|
294
|
+
'the rescope branch re-runs the scope wall on the new touches');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// =========================================================================
|
|
298
|
+
// 5. the route-rank pin
|
|
299
|
+
// =========================================================================
|
|
300
|
+
|
|
301
|
+
await test('route-rank pin: POST /goals/:id/conflicts/resolve stays any-builder (in-handler wall)', () => {
|
|
302
|
+
assert.equal(MODULE_EXPECTED_RANKS.lifecycle['POST /goals/:id/conflicts/resolve'], 'any-builder');
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
summary();
|