@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,572 @@
|
|
|
1
|
+
// GDS builder-memory store — server-side helpers for a builder's Claude Code
|
|
2
|
+
// per-session memory dir.
|
|
3
|
+
//
|
|
4
|
+
// Implements ADR 0024 (cloneable repo + local-first memory with server-canonical
|
|
5
|
+
// sync) §1 (schema) and the helper-layer half of §4 (per-builder isolation).
|
|
6
|
+
// Schema lives in migrations/054_builder_memory.sql.
|
|
7
|
+
//
|
|
8
|
+
// Scope of THIS module (task #273 / V3.R54): the storage layer only — the three
|
|
9
|
+
// helpers below, plus quota enforcement and per-builder isolation. The HTTP
|
|
10
|
+
// endpoints that call these (POST /memory/sync, GET /memory/since, GET
|
|
11
|
+
// /memory/files, GET /memory/files/:path) land in #274/#276 (src/bongos/routes/
|
|
12
|
+
// memory.js); the /builder-ship push wiring lands in #274; the pull-on-start
|
|
13
|
+
// wiring lands in #275. Those tasks build ON these helpers.
|
|
14
|
+
//
|
|
15
|
+
// Isolation (ADR 0024 §4): EVERY helper here is scoped by builder_id — there is
|
|
16
|
+
// no helper that reads or lists across builders. A caller physically cannot use
|
|
17
|
+
// this module to read another builder's files, because builder_id is a required
|
|
18
|
+
// argument on every function and is part of the table's PRIMARY KEY. The route
|
|
19
|
+
// layer (#276) adds the matching req.builder.id === path_owner check and the
|
|
20
|
+
// Archon-only audit-logged forensic read; this layer is the floor beneath it.
|
|
21
|
+
// Same trust-boundary posture as ADR 0016: enforce server-side, trust nothing a
|
|
22
|
+
// builder can edit locally.
|
|
23
|
+
//
|
|
24
|
+
// Like db.js, this module is pure SQL plumbing over the shared Pool — routing
|
|
25
|
+
// and auth live elsewhere.
|
|
26
|
+
|
|
27
|
+
const crypto = require('crypto');
|
|
28
|
+
// Carved into modules/memory/ (BV1.R72 / ADR 0091 §2): reach the shared pool
|
|
29
|
+
// through the published doorway — a module never requires a core internal.
|
|
30
|
+
const { pool } = require('../../src/module-api');
|
|
31
|
+
// ADR 0095 C4: <private>…</private>-fenced content is stripped at ingest so it
|
|
32
|
+
// is never persisted to builder memory (nor, via the chunker, indexed). Pure +
|
|
33
|
+
// intra-module require (the chunker is the module's hermetic text-transform).
|
|
34
|
+
const { stripPrivate } = require('./search-chunker');
|
|
35
|
+
|
|
36
|
+
// -------------------------------------------------------------------------
|
|
37
|
+
// Quotas (ADR 0024 §1) — enforced HERE, not by DB constraints, so the API can
|
|
38
|
+
// return a clean 413 with a prune hint rather than a constraint-violation 500.
|
|
39
|
+
// The numbers are deliberate over-estimates (current observed memory dirs are
|
|
40
|
+
// kilobytes / ~3 files); if real usage outgrows them we raise the cap or prune,
|
|
41
|
+
// neither of which is load-bearing.
|
|
42
|
+
// -------------------------------------------------------------------------
|
|
43
|
+
const MAX_FILE_BYTES = 1 * 1024 * 1024; // 1 MB per file
|
|
44
|
+
const MAX_BUILDER_BYTES = 50 * 1024 * 1024; // 50 MB total per builder
|
|
45
|
+
const MAX_BUILDER_FILES = 2000; // 2000 files per builder
|
|
46
|
+
|
|
47
|
+
// Reject a path that tries to escape the builder's memory dir. The store keys
|
|
48
|
+
// by (builder_id, relative-path) and never touches the local filesystem, so
|
|
49
|
+
// path traversal can't read another builder's *rows* — but a '..' or absolute
|
|
50
|
+
// path is still a malformed key we don't want to persist (it would make the
|
|
51
|
+
// pull side write outside the memory dir on restore). Belt-and-braces with the
|
|
52
|
+
// route layer's own validation.
|
|
53
|
+
function assertValidPath(path) {
|
|
54
|
+
if (typeof path !== 'string' || path.length === 0) {
|
|
55
|
+
throw { code: 'BAD_PATH', message: 'path must be a non-empty string' };
|
|
56
|
+
}
|
|
57
|
+
if (path.startsWith('/') || path.includes('..') || path.includes('\0') || path.includes('\\')) {
|
|
58
|
+
throw { code: 'BAD_PATH', message: 'path must be a relative path inside the memory dir (no leading /, no "..", no backslash, no NUL)' };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function sha256Hex(content) {
|
|
63
|
+
return crypto.createHash('sha256').update(content, 'utf8').digest('hex');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Normalize a client-supplied mtime into a valid Date or null. mtime is
|
|
67
|
+
// advisory (the server clock is the conflict authority), so an unparseable
|
|
68
|
+
// value must NOT throw — we store NULL and let the row's own updated_at be the
|
|
69
|
+
// truth. Guards against `new Date('garbage')` producing an Invalid Date that pg
|
|
70
|
+
// would choke on.
|
|
71
|
+
function normalizeMtime(mtime) {
|
|
72
|
+
if (mtime == null) return null;
|
|
73
|
+
const d = mtime instanceof Date ? mtime : new Date(mtime);
|
|
74
|
+
if (isNaN(d.getTime())) return null;
|
|
75
|
+
return d;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// storeMemoryFile — upsert one file for one builder (ADR 0024 §1 + §3).
|
|
79
|
+
//
|
|
80
|
+
// Per-file quota is checked first (1 MB). Then the per-builder caps (50 MB
|
|
81
|
+
// total, 2000 files) are checked transactionally against the builder's CURRENT
|
|
82
|
+
// stored set, treating this write as a replace-or-add of `path` — so re-saving
|
|
83
|
+
// an existing file never trips the count cap, and the byte cap is computed on
|
|
84
|
+
// the post-write total (current total − old size of this path + new size).
|
|
85
|
+
//
|
|
86
|
+
// Conflict policy (§3) is last-write-wins, server clock. We do NOT compare
|
|
87
|
+
// mtimes here and reject — the route layer decides whether an incoming push is
|
|
88
|
+
// "newer" before calling this; this helper unconditionally writes what it's
|
|
89
|
+
// given (an UPSERT). The stored `mtime` is the client's reported file mtime
|
|
90
|
+
// (for the pull side's newer-than check); the server's own "when did this row
|
|
91
|
+
// change" truth is updated_at (trigger-maintained).
|
|
92
|
+
//
|
|
93
|
+
// Returns the stored row's metadata (no content echo). Throws:
|
|
94
|
+
// { code:'BAD_PATH' } — malformed path
|
|
95
|
+
// { code:'FILE_TOO_LARGE', byteLen, max } — single file over 1 MB
|
|
96
|
+
// { code:'MEMORY_QUOTA_EXCEEDED', kind, ... } — would exceed 50 MB or 2000 files
|
|
97
|
+
async function storeMemoryFile(builderId, path, content, opts = {}) {
|
|
98
|
+
assertValidPath(path);
|
|
99
|
+
// ADR 0095 C4: strip <private>-fenced content before it is sized/hashed/stored.
|
|
100
|
+
const body = stripPrivate(typeof content === 'string' ? content : String(content ?? ''));
|
|
101
|
+
const byteLen = Buffer.byteLength(body, 'utf8');
|
|
102
|
+
|
|
103
|
+
if (byteLen > MAX_FILE_BYTES) {
|
|
104
|
+
throw { code: 'FILE_TOO_LARGE', byteLen, max: MAX_FILE_BYTES, path };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const sha256 = sha256Hex(body);
|
|
108
|
+
// mtime: caller may pass the source file's mtime; default to server now().
|
|
109
|
+
// An unparseable mtime is normalized to NULL (advisory field — never throws).
|
|
110
|
+
const mtime = normalizeMtime(opts.mtime);
|
|
111
|
+
|
|
112
|
+
const client = await pool.connect();
|
|
113
|
+
try {
|
|
114
|
+
await client.query('BEGIN');
|
|
115
|
+
|
|
116
|
+
// Lock this builder's rows for the duration so the quota check and the
|
|
117
|
+
// upsert see a consistent snapshot (two concurrent pushes from the same
|
|
118
|
+
// builder can't both slip under the cap).
|
|
119
|
+
const { rows: existing } = await client.query(
|
|
120
|
+
`SELECT path, byte_len FROM builder_memory
|
|
121
|
+
WHERE builder_id = $1
|
|
122
|
+
FOR UPDATE`,
|
|
123
|
+
[builderId]
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const currentTotalBytes = existing.reduce((sum, r) => sum + Number(r.byte_len), 0);
|
|
127
|
+
const oldRow = existing.find((r) => r.path === path);
|
|
128
|
+
const isReplace = oldRow !== undefined;
|
|
129
|
+
const oldBytes = isReplace ? Number(oldRow.byte_len) : 0;
|
|
130
|
+
|
|
131
|
+
// File-count cap only bites when ADDING a new path.
|
|
132
|
+
if (!isReplace && existing.length >= MAX_BUILDER_FILES) {
|
|
133
|
+
throw {
|
|
134
|
+
code: 'MEMORY_QUOTA_EXCEEDED',
|
|
135
|
+
kind: 'file_count',
|
|
136
|
+
current: existing.length,
|
|
137
|
+
max: MAX_BUILDER_FILES,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Byte cap on the post-write total.
|
|
142
|
+
const projectedTotal = currentTotalBytes - oldBytes + byteLen;
|
|
143
|
+
if (projectedTotal > MAX_BUILDER_BYTES) {
|
|
144
|
+
throw {
|
|
145
|
+
code: 'MEMORY_QUOTA_EXCEEDED',
|
|
146
|
+
kind: 'total_bytes',
|
|
147
|
+
currentBytes: currentTotalBytes,
|
|
148
|
+
projectedBytes: projectedTotal,
|
|
149
|
+
max: MAX_BUILDER_BYTES,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const { rows } = await client.query(
|
|
154
|
+
`INSERT INTO builder_memory (builder_id, path, content, mtime, sha256, byte_len)
|
|
155
|
+
VALUES ($1, $2, $3, COALESCE($4, now()), $5, $6)
|
|
156
|
+
ON CONFLICT (builder_id, path) DO UPDATE
|
|
157
|
+
SET content = EXCLUDED.content,
|
|
158
|
+
mtime = EXCLUDED.mtime,
|
|
159
|
+
sha256 = EXCLUDED.sha256,
|
|
160
|
+
byte_len = EXCLUDED.byte_len
|
|
161
|
+
RETURNING builder_id, path, mtime, sha256, byte_len, created_at, updated_at`,
|
|
162
|
+
[builderId, path, body, mtime, sha256, byteLen]
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
await client.query('COMMIT');
|
|
166
|
+
return rows[0];
|
|
167
|
+
} catch (err) {
|
|
168
|
+
await client.query('ROLLBACK').catch(() => {});
|
|
169
|
+
throw err;
|
|
170
|
+
} finally {
|
|
171
|
+
client.release();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// storeMemoryFilesBatch — upsert MANY files for one builder in ONE transaction.
|
|
176
|
+
//
|
|
177
|
+
// Same semantics as storeMemoryFile, but the whole batch shares a single
|
|
178
|
+
// BEGIN/COMMIT (the route used to open one transaction PER file — N+1). The
|
|
179
|
+
// per-builder quota (50 MB total, 2000 files) is computed ONCE against the
|
|
180
|
+
// builder's currently-stored rows, then the batch's effect is folded in as we
|
|
181
|
+
// walk it — so the cap accounts for the whole batch, not each file in
|
|
182
|
+
// isolation. Conflict policy is unchanged (last-write-wins UPSERT).
|
|
183
|
+
//
|
|
184
|
+
// Failure model matches the single-file helper, but per-file: instead of
|
|
185
|
+
// throwing on the first bad file (which would roll back the whole batch and
|
|
186
|
+
// punish the good files), each file gets its own result entry. A file that
|
|
187
|
+
// trips a per-file or per-builder limit is recorded with the SAME error shape
|
|
188
|
+
// storeMemoryFile throws ({ code, ... }); the good files still commit. This
|
|
189
|
+
// lets the route map errors exactly as it does for the single-file path.
|
|
190
|
+
//
|
|
191
|
+
// `files` is [ { path, content, mtime? }, ... ]. Returns a results array, one
|
|
192
|
+
// entry per input file IN ORDER:
|
|
193
|
+
// { ok: true, row } — stored (row is the upserted metadata)
|
|
194
|
+
// { ok: false, error: { code, ... } } — rejected (BAD_PATH / FILE_TOO_LARGE /
|
|
195
|
+
// MEMORY_QUOTA_EXCEEDED), same codes as
|
|
196
|
+
// storeMemoryFile.
|
|
197
|
+
// Content that isn't a string is the route's concern (BAD_CONTENT) — it filters
|
|
198
|
+
// those before calling, so here every entry is assumed to have string content.
|
|
199
|
+
async function storeMemoryFilesBatch(builderId, files) {
|
|
200
|
+
const entries = Array.isArray(files) ? files : [];
|
|
201
|
+
const results = new Array(entries.length);
|
|
202
|
+
|
|
203
|
+
const client = await pool.connect();
|
|
204
|
+
try {
|
|
205
|
+
await client.query('BEGIN');
|
|
206
|
+
|
|
207
|
+
// Snapshot + lock this builder's rows once for the whole batch, so the
|
|
208
|
+
// quota check sees a consistent view and concurrent pushes can't both slip
|
|
209
|
+
// under the cap.
|
|
210
|
+
const { rows: existing } = await client.query(
|
|
211
|
+
`SELECT path, byte_len FROM builder_memory
|
|
212
|
+
WHERE builder_id = $1
|
|
213
|
+
FOR UPDATE`,
|
|
214
|
+
[builderId]
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// Running totals start from what's already stored, then fold in each
|
|
218
|
+
// accepted file in this batch (a replace swaps old bytes for new and keeps
|
|
219
|
+
// the file count flat; a new path adds bytes and bumps the count).
|
|
220
|
+
let runningBytes = existing.reduce((sum, r) => sum + Number(r.byte_len), 0);
|
|
221
|
+
let runningFiles = existing.length;
|
|
222
|
+
// Track sizes of paths we've already touched IN THIS BATCH so a second
|
|
223
|
+
// write to the same path within one batch is treated as a replace, not an
|
|
224
|
+
// add (mirrors the single-file replace-or-add math).
|
|
225
|
+
const knownBytes = new Map();
|
|
226
|
+
for (const r of existing) knownBytes.set(r.path, Number(r.byte_len));
|
|
227
|
+
|
|
228
|
+
for (let i = 0; i < entries.length; i++) {
|
|
229
|
+
const f = entries[i];
|
|
230
|
+
const path = f && f.path;
|
|
231
|
+
const content = f && f.content;
|
|
232
|
+
try {
|
|
233
|
+
assertValidPath(path);
|
|
234
|
+
// ADR 0095 C4: strip <private>-fenced content per file before sizing/storing.
|
|
235
|
+
const body = stripPrivate(typeof content === 'string' ? content : String(content ?? ''));
|
|
236
|
+
const byteLen = Buffer.byteLength(body, 'utf8');
|
|
237
|
+
|
|
238
|
+
if (byteLen > MAX_FILE_BYTES) {
|
|
239
|
+
throw { code: 'FILE_TOO_LARGE', byteLen, max: MAX_FILE_BYTES, path };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const isReplace = knownBytes.has(path);
|
|
243
|
+
const oldBytes = isReplace ? knownBytes.get(path) : 0;
|
|
244
|
+
|
|
245
|
+
// File-count cap only bites when ADDING a new path.
|
|
246
|
+
if (!isReplace && runningFiles >= MAX_BUILDER_FILES) {
|
|
247
|
+
throw {
|
|
248
|
+
code: 'MEMORY_QUOTA_EXCEEDED',
|
|
249
|
+
kind: 'file_count',
|
|
250
|
+
current: runningFiles,
|
|
251
|
+
max: MAX_BUILDER_FILES,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Byte cap on the post-write running total.
|
|
256
|
+
const projectedTotal = runningBytes - oldBytes + byteLen;
|
|
257
|
+
if (projectedTotal > MAX_BUILDER_BYTES) {
|
|
258
|
+
throw {
|
|
259
|
+
code: 'MEMORY_QUOTA_EXCEEDED',
|
|
260
|
+
kind: 'total_bytes',
|
|
261
|
+
currentBytes: runningBytes,
|
|
262
|
+
projectedBytes: projectedTotal,
|
|
263
|
+
max: MAX_BUILDER_BYTES,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const sha256 = sha256Hex(body);
|
|
268
|
+
const mtime = normalizeMtime(f && f.mtime);
|
|
269
|
+
|
|
270
|
+
const { rows } = await client.query(
|
|
271
|
+
`INSERT INTO builder_memory (builder_id, path, content, mtime, sha256, byte_len)
|
|
272
|
+
VALUES ($1, $2, $3, COALESCE($4, now()), $5, $6)
|
|
273
|
+
ON CONFLICT (builder_id, path) DO UPDATE
|
|
274
|
+
SET content = EXCLUDED.content,
|
|
275
|
+
mtime = EXCLUDED.mtime,
|
|
276
|
+
sha256 = EXCLUDED.sha256,
|
|
277
|
+
byte_len = EXCLUDED.byte_len
|
|
278
|
+
RETURNING builder_id, path, mtime, sha256, byte_len, created_at, updated_at`,
|
|
279
|
+
[builderId, path, body, mtime, sha256, byteLen]
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
// Commit the accepted file's effect to the running totals.
|
|
283
|
+
runningBytes = projectedTotal;
|
|
284
|
+
if (!isReplace) runningFiles += 1;
|
|
285
|
+
knownBytes.set(path, byteLen);
|
|
286
|
+
|
|
287
|
+
results[i] = { ok: true, row: rows[0] };
|
|
288
|
+
} catch (err) {
|
|
289
|
+
// Per-file rejection: record it, leave the rest of the batch alone.
|
|
290
|
+
// The error object carries the same `code` storeMemoryFile throws.
|
|
291
|
+
results[i] = { ok: false, error: err };
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
await client.query('COMMIT');
|
|
296
|
+
return results;
|
|
297
|
+
} catch (err) {
|
|
298
|
+
await client.query('ROLLBACK').catch(() => {});
|
|
299
|
+
throw err;
|
|
300
|
+
} finally {
|
|
301
|
+
client.release();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// fetchMemoryFile — read ONE file for ONE builder (ADR 0024 §4 isolation).
|
|
306
|
+
//
|
|
307
|
+
// Scoped by builder_id in the WHERE clause: there is no way to read another
|
|
308
|
+
// builder's file through this helper. Returns the full row (content included)
|
|
309
|
+
// or null if this builder has no file at that path. A path belonging to a
|
|
310
|
+
// DIFFERENT builder returns null here too — it's simply not in this builder's
|
|
311
|
+
// keyspace.
|
|
312
|
+
async function fetchMemoryFile(builderId, path) {
|
|
313
|
+
assertValidPath(path);
|
|
314
|
+
const { rows } = await pool.query(
|
|
315
|
+
`SELECT builder_id, path, content, mtime, sha256, byte_len, created_at, updated_at
|
|
316
|
+
FROM builder_memory
|
|
317
|
+
WHERE builder_id = $1 AND path = $2`,
|
|
318
|
+
[builderId, path]
|
|
319
|
+
);
|
|
320
|
+
return rows[0] ?? null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// listMemoryFiles — list a builder's files (metadata only, no content) so the
|
|
324
|
+
// pull side (#275) can decide what to fetch without dragging every body over
|
|
325
|
+
// the wire (ADR 0024 §2/§4). Scoped by builder_id — never lists across
|
|
326
|
+
// builders. Newest-changed first (matches the (builder_id, updated_at DESC)
|
|
327
|
+
// index). Returns [] for a builder with no stored memory.
|
|
328
|
+
async function listMemoryFiles(builderId) {
|
|
329
|
+
// Defensive LIMIT: the per-builder file cap (MAX_BUILDER_FILES) is enforced on
|
|
330
|
+
// write, so a builder can never hold more than that many rows — but bounding
|
|
331
|
+
// the SELECT too means a single list query stays cheap even if the cap were
|
|
332
|
+
// ever bypassed (manual SQL insert, a future code path, etc.). Metadata-only,
|
|
333
|
+
// so the bound is essentially free.
|
|
334
|
+
const { rows } = await pool.query(
|
|
335
|
+
`SELECT builder_id, path, mtime, sha256, byte_len, created_at, updated_at
|
|
336
|
+
FROM builder_memory
|
|
337
|
+
WHERE builder_id = $1
|
|
338
|
+
ORDER BY updated_at DESC, path ASC
|
|
339
|
+
LIMIT $2`,
|
|
340
|
+
[builderId, MAX_BUILDER_FILES]
|
|
341
|
+
);
|
|
342
|
+
return rows;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// -------------------------------------------------------------------------
|
|
346
|
+
// BFG cross-builder WRITE (ADR 0026 §6C / task #446) — the first cross-builder
|
|
347
|
+
// memory write path. Everything here is reachable ONLY through the
|
|
348
|
+
// principal-gated route (src/bongos/routes/memory.js, requireBfgPrincipal); the
|
|
349
|
+
// helpers themselves enforce the namespace + attribution + audit invariants so
|
|
350
|
+
// the guarantee can't be bypassed by a future careless caller.
|
|
351
|
+
// -------------------------------------------------------------------------
|
|
352
|
+
|
|
353
|
+
// Reserved namespace: every BFG-written file lives under this prefix, and the
|
|
354
|
+
// SELF sync path (POST /memory/sync) REJECTS writes under it. That reservation
|
|
355
|
+
// is what makes "the BFG never overwrites a builder's own file" airtight —
|
|
356
|
+
// `bfg/` is exclusively the BFG's keyspace, so there is no collision to resolve.
|
|
357
|
+
// A builder still READS and can DELETE files here (it's their own keyspace); the
|
|
358
|
+
// frontmatter (author: BFG) self-announces who wrote them.
|
|
359
|
+
const BFG_NAMESPACE_PREFIX = 'bfg/';
|
|
360
|
+
const BFG_KINDS = new Set(['dream', 'correction']);
|
|
361
|
+
|
|
362
|
+
// assertBfgPath — a BFG write target must be a valid relative path AND live
|
|
363
|
+
// under the reserved bfg/ namespace. Layered on assertValidPath (no traversal).
|
|
364
|
+
function assertBfgPath(path) {
|
|
365
|
+
assertValidPath(path);
|
|
366
|
+
if (!path.startsWith(BFG_NAMESPACE_PREFIX)) {
|
|
367
|
+
throw { code: 'NOT_BFG_NAMESPACE', message: `BFG writes must live under '${BFG_NAMESPACE_PREFIX}'` };
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// isReservedNamespacePath — the SELF sync path calls this to refuse a builder
|
|
372
|
+
// writing into the BFG's reserved namespace. Pure + exported for the route + tests.
|
|
373
|
+
function isReservedNamespacePath(path) {
|
|
374
|
+
return typeof path === 'string' && path.startsWith(BFG_NAMESPACE_PREFIX);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// buildBfgMemoryFile — PURE constructor for a BFG-authored memory file. Returns
|
|
378
|
+
// { path, content }. The attribution is structural, not optional: the caller
|
|
379
|
+
// supplies semantic fields (kind/slug/title/body/rationale) and this function
|
|
380
|
+
// emits the mandatory self-announcing frontmatter + banner, so a BFG write can
|
|
381
|
+
// NEVER masquerade as the builder's own note (ADR 0026 §6C). Exported + unit-
|
|
382
|
+
// tested. `now` is injectable for deterministic tests.
|
|
383
|
+
function buildBfgMemoryFile({ kind, slug, title, body_md, rationale, sourceBuilder, targetBuilderId, now }) {
|
|
384
|
+
if (!BFG_KINDS.has(kind)) {
|
|
385
|
+
throw { code: 'BAD_BFG_KIND', message: `kind must be one of: ${[...BFG_KINDS].join(', ')}` };
|
|
386
|
+
}
|
|
387
|
+
if (typeof slug !== 'string' || !/^[a-z0-9][a-z0-9-]{0,80}$/.test(slug)) {
|
|
388
|
+
throw { code: 'BAD_BFG_SLUG', message: 'slug must be kebab-case ([a-z0-9-], starts alnum, <=81 chars)' };
|
|
389
|
+
}
|
|
390
|
+
if (typeof body_md !== 'string' || body_md.trim() === '') {
|
|
391
|
+
throw { code: 'BAD_BFG_BODY', message: 'body_md is required (non-empty string)' };
|
|
392
|
+
}
|
|
393
|
+
if (typeof rationale !== 'string' || rationale.trim() === '') {
|
|
394
|
+
throw { code: 'BAD_BFG_RATIONALE', message: 'rationale is required (non-empty string)' };
|
|
395
|
+
}
|
|
396
|
+
const safeTitle = typeof title === 'string' && title.trim() ? title.trim() : `BFG ${kind}: ${slug}`;
|
|
397
|
+
const date = (now instanceof Date ? now : new Date(now || Date.now())).toISOString();
|
|
398
|
+
const path = `${BFG_NAMESPACE_PREFIX}${kind}-${slug}.md`;
|
|
399
|
+
// JSON.stringify yields a safely double-quoted scalar (YAML accepts JSON
|
|
400
|
+
// string syntax), collapsing newlines so a multi-line rationale can't break
|
|
401
|
+
// the frontmatter block.
|
|
402
|
+
const yamlStr = (s) => JSON.stringify(String(s).replace(/\s+/g, ' ').trim());
|
|
403
|
+
const banner =
|
|
404
|
+
kind === 'correction'
|
|
405
|
+
? '> **This is a BFG correction note — written by the BFG (system), not by you.** It names an observed behavior, the rule it touched, and the expected change. It is transparent and attributed by design (ADR 0026 §6C); a karma adjustment accompanies it. You may delete this file.'
|
|
406
|
+
: '> **This is a BFG dream — written by the BFG (system), not by you.** It is a practice the BFG planted because it looked like an obvious improvement worth adopting. It is clearly marked and yours to keep or delete (ADR 0026 §6C).';
|
|
407
|
+
const content = `---
|
|
408
|
+
name: bfg-${kind}-${slug}
|
|
409
|
+
author: BFG
|
|
410
|
+
type: bfg-${kind}
|
|
411
|
+
source_builder: ${yamlStr(sourceBuilder || 'bfg-system')}
|
|
412
|
+
target_builder: ${targetBuilderId}
|
|
413
|
+
date: ${date}
|
|
414
|
+
rationale: ${yamlStr(rationale)}
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
${banner}
|
|
418
|
+
|
|
419
|
+
## ${safeTitle}
|
|
420
|
+
|
|
421
|
+
${body_md.trim()}
|
|
422
|
+
`;
|
|
423
|
+
return { path, content };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// bfgWriteMemoryFile — upsert a BFG-authored file into ANOTHER builder's memory
|
|
427
|
+
// AND write its audit_log row in ONE transaction. This atomicity is the
|
|
428
|
+
// fail-closed guarantee ADR 0026 §6C requires: there is no cross-builder write
|
|
429
|
+
// without a durable audit row, and no audit row that claims a write which rolled
|
|
430
|
+
// back. Same quota math as storeMemoryFile (the write counts against the TARGET
|
|
431
|
+
// builder's quota). The path MUST be in the bfg/ namespace (assertBfgPath).
|
|
432
|
+
//
|
|
433
|
+
// `audit` carries the principal + semantics: { principalId, route, action,
|
|
434
|
+
// kind, rationale, responseStatus, ip, userAgent }. The audit row is attributed
|
|
435
|
+
// to the PRINCIPAL (builder_id = principalId), method 'POST' (the audit_log
|
|
436
|
+
// CHECK forbids 'GET'-style verbs), with the true semantics in the redacted body.
|
|
437
|
+
async function bfgWriteMemoryFile({ targetBuilderId, path, content, audit, karma }) {
|
|
438
|
+
assertBfgPath(path);
|
|
439
|
+
// ADR 0095 C4: strip <private>-fenced content even on the BFG cross-builder
|
|
440
|
+
// write — a planted dream/correction must not smuggle fenced content past the
|
|
441
|
+
// marker into another builder's keyspace.
|
|
442
|
+
const body = stripPrivate(typeof content === 'string' ? content : String(content ?? ''));
|
|
443
|
+
const byteLen = Buffer.byteLength(body, 'utf8');
|
|
444
|
+
if (byteLen > MAX_FILE_BYTES) {
|
|
445
|
+
throw { code: 'FILE_TOO_LARGE', byteLen, max: MAX_FILE_BYTES, path };
|
|
446
|
+
}
|
|
447
|
+
const sha256 = sha256Hex(body);
|
|
448
|
+
const a = audit || {};
|
|
449
|
+
|
|
450
|
+
const client = await pool.connect();
|
|
451
|
+
try {
|
|
452
|
+
await client.query('BEGIN');
|
|
453
|
+
|
|
454
|
+
// Lock the TARGET builder's rows so the quota check + upsert see a consistent
|
|
455
|
+
// snapshot (mirrors storeMemoryFile).
|
|
456
|
+
const { rows: existing } = await client.query(
|
|
457
|
+
`SELECT path, byte_len FROM builder_memory WHERE builder_id = $1 FOR UPDATE`,
|
|
458
|
+
[targetBuilderId]
|
|
459
|
+
);
|
|
460
|
+
const currentTotalBytes = existing.reduce((sum, r) => sum + Number(r.byte_len), 0);
|
|
461
|
+
const oldRow = existing.find((r) => r.path === path);
|
|
462
|
+
const isReplace = oldRow !== undefined;
|
|
463
|
+
if (!isReplace && existing.length >= MAX_BUILDER_FILES) {
|
|
464
|
+
throw { code: 'MEMORY_QUOTA_EXCEEDED', kind: 'file_count', current: existing.length, max: MAX_BUILDER_FILES };
|
|
465
|
+
}
|
|
466
|
+
const projectedTotal = currentTotalBytes - (isReplace ? Number(oldRow.byte_len) : 0) + byteLen;
|
|
467
|
+
if (projectedTotal > MAX_BUILDER_BYTES) {
|
|
468
|
+
throw { code: 'MEMORY_QUOTA_EXCEEDED', kind: 'total_bytes', currentBytes: currentTotalBytes, projectedBytes: projectedTotal, max: MAX_BUILDER_BYTES };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const { rows } = await client.query(
|
|
472
|
+
`INSERT INTO builder_memory (builder_id, path, content, mtime, sha256, byte_len)
|
|
473
|
+
VALUES ($1, $2, $3, now(), $4, $5)
|
|
474
|
+
ON CONFLICT (builder_id, path) DO UPDATE
|
|
475
|
+
SET content = EXCLUDED.content,
|
|
476
|
+
mtime = EXCLUDED.mtime,
|
|
477
|
+
sha256 = EXCLUDED.sha256,
|
|
478
|
+
byte_len = EXCLUDED.byte_len
|
|
479
|
+
RETURNING builder_id, path, mtime, sha256, byte_len, created_at, updated_at`,
|
|
480
|
+
[targetBuilderId, path, body, sha256, byteLen]
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
// Fail-closed audit, same transaction. Shape mirrors db.insertAuditLog so the
|
|
484
|
+
// row is indistinguishable from a middleware-written one to the audit reader;
|
|
485
|
+
// the true semantics (action, target, path, rationale) live in the redacted
|
|
486
|
+
// body — auditors query by `action`, not `method`.
|
|
487
|
+
// NOTE: the global audit middleware ALSO logs this POST (a second, generic
|
|
488
|
+
// row with method=POST and no `action`). That's harmless redundancy — THIS
|
|
489
|
+
// in-transaction row is the authoritative one and the only one guaranteed to
|
|
490
|
+
// exist iff the write committed. Auditors filter on action='bfg_*_write'.
|
|
491
|
+
const { rows: auditRows } = await client.query(
|
|
492
|
+
`INSERT INTO audit_log (builder_id, route, method, request_body_redacted, response_status, ip, user_agent)
|
|
493
|
+
VALUES ($1, $2, 'POST', $3, $4, $5, $6)
|
|
494
|
+
RETURNING id, recorded_at`,
|
|
495
|
+
[
|
|
496
|
+
a.principalId ?? null,
|
|
497
|
+
a.route ?? '/api/gds/memory/builders/:builderId/bfg-write',
|
|
498
|
+
{
|
|
499
|
+
action: a.action || 'bfg_dream_write',
|
|
500
|
+
http_method: 'POST',
|
|
501
|
+
target_builder_id: targetBuilderId,
|
|
502
|
+
path,
|
|
503
|
+
kind: a.kind ?? null,
|
|
504
|
+
rationale: a.rationale ?? null,
|
|
505
|
+
},
|
|
506
|
+
a.responseStatus ?? 201,
|
|
507
|
+
a.ip ?? null,
|
|
508
|
+
a.userAgent ?? null,
|
|
509
|
+
]
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
// 6C.2 — a CORRECTION carries a negative karma delta in the SAME transaction
|
|
513
|
+
// as its visible note + audit row (ADR 0026 §6C: "a visible BFG-authored
|
|
514
|
+
// corrective note PLUS a negative karma_log delta"). The gds_apply_karma
|
|
515
|
+
// trigger updates builders.karma on this insert; bundling it here means the
|
|
516
|
+
// note, the karma dock, and the audit either all land or all roll back —
|
|
517
|
+
// there is never a dock without its explanatory note, or vice-versa.
|
|
518
|
+
let karmaRow = null;
|
|
519
|
+
if (karma && Number.isFinite(Number(karma.delta)) && Math.trunc(Number(karma.delta)) !== 0) {
|
|
520
|
+
const { rows: kr } = await client.query(
|
|
521
|
+
`INSERT INTO karma_log (builder_id, delta, reason, granted_by)
|
|
522
|
+
VALUES ($1, $2, $3, $4) RETURNING id`,
|
|
523
|
+
[targetBuilderId, Math.trunc(Number(karma.delta)), karma.reason || 'bfg_correction', karma.grantedBy ?? null]
|
|
524
|
+
);
|
|
525
|
+
karmaRow = kr[0];
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
await client.query('COMMIT');
|
|
529
|
+
return { row: rows[0], audit_id: auditRows[0].id, audit_recorded_at: auditRows[0].recorded_at, karma_id: karmaRow ? karmaRow.id : null };
|
|
530
|
+
} catch (err) {
|
|
531
|
+
await client.query('ROLLBACK').catch(() => {});
|
|
532
|
+
throw err;
|
|
533
|
+
} finally {
|
|
534
|
+
client.release();
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// deleteMemoryFile — remove ONE file from ONE builder's memory (ADR 0026 §6C
|
|
539
|
+
// delete affordance). Scoped by builder_id, so the self DELETE route can only
|
|
540
|
+
// ever remove the AUTHENTICATED builder's own file — including a BFG-written
|
|
541
|
+
// dream/correction, which lives in the target builder's keyspace. Returns the
|
|
542
|
+
// number of rows deleted (0 if there was nothing at that path).
|
|
543
|
+
async function deleteMemoryFile(builderId, path) {
|
|
544
|
+
assertValidPath(path);
|
|
545
|
+
const { rowCount } = await pool.query(
|
|
546
|
+
`DELETE FROM builder_memory WHERE builder_id = $1 AND path = $2`,
|
|
547
|
+
[builderId, path]
|
|
548
|
+
);
|
|
549
|
+
return rowCount;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
module.exports = {
|
|
553
|
+
storeMemoryFile,
|
|
554
|
+
storeMemoryFilesBatch,
|
|
555
|
+
fetchMemoryFile,
|
|
556
|
+
listMemoryFiles,
|
|
557
|
+
deleteMemoryFile,
|
|
558
|
+
// exported for the route layer (#274/#276) + tests so the quota numbers and
|
|
559
|
+
// path validation live in exactly one place.
|
|
560
|
+
assertValidPath,
|
|
561
|
+
sha256Hex,
|
|
562
|
+
MAX_FILE_BYTES,
|
|
563
|
+
MAX_BUILDER_BYTES,
|
|
564
|
+
MAX_BUILDER_FILES,
|
|
565
|
+
// BFG cross-builder write (6C.1, ADR 0026 §6C)
|
|
566
|
+
bfgWriteMemoryFile,
|
|
567
|
+
buildBfgMemoryFile,
|
|
568
|
+
assertBfgPath,
|
|
569
|
+
isReservedNamespacePath,
|
|
570
|
+
BFG_NAMESPACE_PREFIX,
|
|
571
|
+
BFG_KINDS,
|
|
572
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"key": "memory",
|
|
3
|
+
"title": "Memory & recall",
|
|
4
|
+
"description": "Per-builder session memory, the lexical recall/search retrieval layer (ADR 0060), and durable learnings. A core-domain module carved out of the GDS core in BV1.R72 (ADR 0091); default-on because every instance needs the build system's memory.",
|
|
5
|
+
"coreVersion": "^1.5.0",
|
|
6
|
+
"default": true,
|
|
7
|
+
"contributes": {
|
|
8
|
+
"routes": ["memory", "search", "learnings"]
|
|
9
|
+
},
|
|
10
|
+
"provides": [],
|
|
11
|
+
"consumes": []
|
|
12
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
// Carved into modules/memory/ (BV1.R72 / ADR 0091 §2): auth + the shared request
|
|
3
|
+
// validators (validateOrRespond/LIMITS) via the doorway; the learnings lib is an
|
|
4
|
+
// intra-module require.
|
|
5
|
+
const api = require('../../../src/module-api');
|
|
6
|
+
const auth = api;
|
|
7
|
+
const learnings = require('../learnings');
|
|
8
|
+
const { validateOrRespond, LIMITS } = api;
|
|
9
|
+
|
|
10
|
+
module.exports = function buildLearningsRouter() {
|
|
11
|
+
const router = express.Router();
|
|
12
|
+
|
|
13
|
+
// rank: any-builder — read learnings (own + others').
|
|
14
|
+
router.get('/learnings', auth.requireBuilder, async (req, res) => {
|
|
15
|
+
const tagsRaw = req.query.tags;
|
|
16
|
+
const touchesRaw = req.query.touches;
|
|
17
|
+
// touches[] cleanup [3/8] (task 878): the preferred claim-time lookup keys
|
|
18
|
+
// off the task's discipline + title (always present) rather than its
|
|
19
|
+
// predicted touches[]. ?touches= is kept for back-compat.
|
|
20
|
+
const disciplineRaw = req.query.discipline;
|
|
21
|
+
const titleRaw = req.query.title;
|
|
22
|
+
try {
|
|
23
|
+
let rows;
|
|
24
|
+
if (tagsRaw) {
|
|
25
|
+
const tags = String(tagsRaw)
|
|
26
|
+
.split(',')
|
|
27
|
+
.map((s) => s.trim().toLowerCase())
|
|
28
|
+
.filter(Boolean);
|
|
29
|
+
rows = await learnings.findLearningsByTags(tags);
|
|
30
|
+
} else if (disciplineRaw || titleRaw) {
|
|
31
|
+
rows = await learnings.findLearningsForTask({
|
|
32
|
+
discipline: disciplineRaw ? String(disciplineRaw) : null,
|
|
33
|
+
title: titleRaw ? String(titleRaw) : null,
|
|
34
|
+
});
|
|
35
|
+
} else if (touchesRaw) {
|
|
36
|
+
const touches = String(touchesRaw).split(',').map((s) => s.trim()).filter(Boolean);
|
|
37
|
+
rows = await learnings.findLearningsForTouches(touches);
|
|
38
|
+
} else {
|
|
39
|
+
rows = await learnings.listAllLearnings();
|
|
40
|
+
}
|
|
41
|
+
res.json({ learnings: rows });
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error('[gds] GET /learnings', err);
|
|
44
|
+
res.fail('list_failed', { status: 500, message: 'internal error' });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// rank: any-builder — capture a learning (own contribution).
|
|
49
|
+
router.post('/learnings', auth.requireBuilder, async (req, res) => {
|
|
50
|
+
if (validateOrRespond(req, res, {
|
|
51
|
+
title: { required: true, type: 'string', maxLength: LIMITS.TITLE, minLength: 1 },
|
|
52
|
+
body_md: { required: true, type: 'string', maxLength: LIMITS.DESCRIPTION, minLength: 1 },
|
|
53
|
+
tags: { type: 'array', maxItems: 64, itemsType: 'string' },
|
|
54
|
+
related_files: { type: 'array', maxItems: 256, itemsType: 'string' },
|
|
55
|
+
source: { type: 'string', maxLength: LIMITS.SOURCE },
|
|
56
|
+
source_ref: { type: 'string', maxLength: LIMITS.SOURCE_REF },
|
|
57
|
+
task_id: { type: 'integer', min: 1 },
|
|
58
|
+
})) return;
|
|
59
|
+
const body = req.body || {};
|
|
60
|
+
if (!body.title || !body.body_md) {
|
|
61
|
+
return res.fail('title_and_body_md_required', 400);
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const tags = Array.isArray(body.tags)
|
|
65
|
+
? body.tags.map((t) => String(t).toLowerCase())
|
|
66
|
+
: [];
|
|
67
|
+
const relatedFiles = Array.isArray(body.related_files) ? body.related_files : [];
|
|
68
|
+
const out = await learnings.createLearning({
|
|
69
|
+
title: body.title,
|
|
70
|
+
bodyMd: body.body_md,
|
|
71
|
+
tags,
|
|
72
|
+
relatedFiles,
|
|
73
|
+
capturedBy: req.builder.id,
|
|
74
|
+
source: body.source ?? null,
|
|
75
|
+
sourceRef: body.source_ref ?? null,
|
|
76
|
+
taskId: body.task_id ?? null,
|
|
77
|
+
});
|
|
78
|
+
if (!out) {
|
|
79
|
+
// ON CONFLICT skipped: same (source, source_ref) already captured.
|
|
80
|
+
return res.status(200).json({ skipped: true, reason: 'duplicate_source_ref' });
|
|
81
|
+
}
|
|
82
|
+
res.status(201).json({ learning: out });
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error('[gds] POST /learnings', err);
|
|
85
|
+
res.fail('create_failed', { status: 500, message: 'internal error' });
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return router;
|
|
90
|
+
};
|