@bongos/core 1.18.3 → 1.18.18
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 +1575 -285
- package/.claude/skills/builder-box/SKILL.md +6 -0
- package/.claude/skills/builder-sequence/SKILL.md +97 -0
- package/.claude/skills/builder-ship/SKILL.md +8 -2
- package/.claude/skills/builder-start/SKILL.md +11 -0
- package/.claude/skills/builder-sync/SKILL.md +41 -0
- package/.claude/skills/fix-task/SKILL.md +64 -0
- package/.claude/skills/goal-create/SKILL.md +158 -0
- package/.claude/skills/goal-review/SKILL.md +3 -1
- package/.claude/skills/idea-triage/SKILL.md +14 -8
- package/.claude/skills/merge-mode/SKILL.md +2 -2
- package/.claude/skills/new-project/SKILL.md +1 -0
- package/.claude/skills/planning-session/SKILL.md +45 -18
- package/.env.local.example +4 -3
- package/README.md +4 -2
- package/bin/bongos.js +1 -0
- package/clients/bongos-client/README.md +2 -2
- package/clients/bongos-client/bongos-client.global.js +82 -18
- package/clients/bongos-client/index.cjs +82 -18
- package/clients/bongos-client/index.d.ts +131 -29
- package/clients/bongos-client/index.mjs +82 -18
- package/config/branding.neutral.json +6 -1
- package/config/governance.neutral.json +21 -0
- package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +24 -0
- package/docs/adr/0090-metic-task-authoring.md +1 -1
- package/docs/adr/0144-devbox-rehome-onto-cloudbongos-plane.md +41 -0
- package/docs/adr/0144-federated-single-logout-backchannel.md +42 -0
- package/docs/adr/0145-devbox-app-branding-driven-module.md +44 -0
- package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +62 -0
- package/docs/adr/0146-configurable-reward-mode-cost-plus-only.md +53 -0
- package/docs/adr/0147-standalone-live-docs-regen.md +39 -0
- package/docs/adr/0148-task-scoped-box-source-access.md +48 -0
- package/docs/adr/0149-bongos-dev-local-launcher.md +57 -0
- package/docs/adr/0150-box-first-boot-bringup-vendored-instances.md +68 -0
- package/docs/adr/0151-governance-permissions-as-atom-ranks-as-roles.md +229 -0
- package/docs/adr/0152-landing-gate-served-by-the-core-app.md +92 -0
- package/docs/adr/0152-metic-task-abandonment.md +67 -0
- package/docs/adr/0153-sequential-chain-runner.md +51 -0
- package/docs/adr/0154-goal-owner-criterion-authoring.md +86 -0
- package/docs/adr/README.md +10 -0
- package/docs/api/openapi.json +843 -17
- package/docs/api-reference.md +23 -10
- package/docs/architecture.md +4 -2
- package/docs/branding-contract.md +3 -1
- package/docs/canonical-permissions.md +21 -8
- package/docs/file-map.md +8 -0
- package/docs/onboarding/diagrams/01-task-lifecycle.mmd +2 -2
- package/docs/onboarding/diagrams/02-rank-ladder.mmd +3 -1
- package/docs/onboarding/diagrams/05-versioning.mmd +1 -3
- package/docs/onboarding/diagrams/assertions.json +4 -7
- package/docs/onboarding/drachmae-and-karma.md +3 -3
- package/docs/onboarding/primer.md +17 -16
- package/docs/onboarding/slash-commands.md +124 -0
- package/docs/recipes/builder-box-lifecycle.md +23 -0
- package/docs/recipes/devbox-app-release.md +14 -4
- package/docs/recipes/instance-first-run.md +16 -0
- package/docs/recipes/local-dev.md +81 -0
- package/docs/recipes/ops-gotchas.md +19 -1
- package/docs/recipes/overnight-builder.md +1 -1
- package/docs/recipes/parallel-goal-waves.md +174 -0
- package/docs/recipes/ship-and-regrade.md +2 -2
- package/docs/recipes/standalone-live-docs.md +48 -0
- package/docs/recipes/work-on-a-task-from-your-dev-box.md +108 -0
- package/migrations/003_pms.sql +6 -7
- package/migrations/006_pms_v2.sql +3 -31
- package/migrations/core_193_task_needs_rebase.sql +28 -0
- package/migrations/core_194_confirmed_backlog_clawback.sql +63 -0
- package/migrations/core_195_idea_triage_provenance.sql +57 -0
- package/migrations/core_196_session_reward_topup.sql +69 -0
- package/modules/autonomy/pollers/routine-timers.js +9 -8
- package/modules/builder-settings/wandering-prefs.js +61 -18
- package/modules/dev-box/app/README.md +73 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/win-alert.png +0 -0
- package/modules/dev-box/app/assets/tray/win-busy.png +0 -0
- package/modules/dev-box/app/assets/tray/win-off.png +0 -0
- package/modules/dev-box/app/assets/tray/win-on.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background@2x.png +0 -0
- package/modules/dev-box/app/build-resources/icon.png +0 -0
- package/modules/dev-box/app/electron-builder.tmpl.yml +95 -0
- package/modules/dev-box/app/electron-builder.yml +95 -0
- package/modules/dev-box/app/package-lock.json +5291 -0
- package/modules/dev-box/app/package.json +30 -0
- package/modules/dev-box/app/renderer/app.js +319 -0
- package/modules/dev-box/app/renderer/index.html +45 -0
- package/modules/dev-box/app/renderer/styles.css +246 -0
- package/modules/dev-box/app/renderer/theme.generated.css +16 -0
- package/modules/dev-box/app/scripts/apply-dmg-backdrop.sh +82 -0
- package/modules/dev-box/app/scripts/dmgbuild-settings.py +39 -0
- package/modules/dev-box/app/scripts/gen-brand.js +138 -0
- package/modules/dev-box/app/scripts/gen-dmg-backdrop.py +323 -0
- package/modules/dev-box/app/scripts/gen-icons.js +286 -0
- package/modules/dev-box/app/scripts/vendor-client.js +23 -0
- package/modules/dev-box/app/src/box-controller.js +287 -0
- package/modules/dev-box/app/src/brand.generated.json +13 -0
- package/modules/dev-box/app/src/brand.js +16 -0
- package/modules/dev-box/app/src/config.js +19 -0
- package/modules/dev-box/app/src/gds-client.js +90 -0
- package/modules/dev-box/app/src/main.js +610 -0
- package/modules/dev-box/app/src/pairing.js +57 -0
- package/modules/dev-box/app/src/preload.js +29 -0
- package/modules/dev-box/app/src/session-store.js +58 -0
- package/modules/dev-box/app/src/ssh-setup.js +569 -0
- package/modules/dev-box/app/src/task-picker.js +70 -0
- package/modules/dev-box/app/src/tray.js +99 -0
- package/modules/dev-box/app/src/updater.js +55 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +698 -0
- package/modules/dev-box/app/test/unit.mjs +526 -0
- package/modules/dev-box/box-access.js +121 -4
- package/modules/dev-box/boxes.js +20 -0
- package/modules/dev-box/module.json +1 -1
- package/modules/dev-box/routes/box.js +87 -3
- package/modules/economy/credits.js +116 -25
- package/modules/economy/reward.js +8 -0
- package/modules/economy/streak.js +2 -1
- package/modules/governance/CLAUDE.md +15 -0
- package/modules/governance/catalog.js +419 -0
- package/modules/governance/config.js +174 -0
- package/modules/governance/db.js +125 -0
- package/modules/governance/migrations/governance_001_tables.sql +81 -0
- package/modules/governance/migrations/governance_002_seed_and_backfill.sql +103 -0
- package/modules/governance/migrations/governance_003_task_abandon_metic.sql +28 -0
- package/modules/governance/migrations/governance_004_criterion_create.sql +39 -0
- package/modules/governance/migrations/governance_005_resync_builder_rank_roles.sql +56 -0
- package/modules/governance/module.json +10 -0
- package/modules/governance/protected-surfaces.js +88 -0
- package/modules/governance/protected-surfaces.json +80 -0
- package/modules/governance/rank-roles.js +83 -0
- package/modules/governance/resolver.js +221 -0
- package/modules/governance/routes/governance.js +89 -0
- package/modules/governance/routes/role-sync.js +49 -0
- package/modules/governance/session-scopes.js +127 -0
- package/modules/governance/session-scopes.json +38 -0
- package/modules/hall-ui/public/atlas.css +117 -0
- package/modules/hall-ui/public/atlas.html +3 -114
- package/modules/hall-ui/public/brand-apply.js +34 -6
- package/modules/hall-ui/public/builders.js +187 -219
- package/modules/hall-ui/public/diagrams.html +2 -0
- package/modules/hall-ui/public/dom-utils.js +27 -0
- package/modules/hall-ui/public/drachmae.html +2 -0
- package/modules/hall-ui/public/drachmae.js +54 -1
- package/modules/hall-ui/public/gate.css +98 -0
- package/modules/hall-ui/public/gate.html +3 -95
- package/modules/hall-ui/public/goal-inbox.js +4 -1
- package/modules/hall-ui/public/goals-page.js +626 -114
- package/modules/hall-ui/public/goals.css +79 -1
- package/modules/hall-ui/public/goals.html +38 -11
- package/modules/hall-ui/public/hall-widgets.js +62 -52
- package/modules/hall-ui/public/harbor.css +123 -0
- package/modules/hall-ui/public/harbor.html +3 -120
- package/modules/hall-ui/public/index.html +11 -4
- package/modules/hall-ui/public/instances-panel.js +4 -1
- package/modules/hall-ui/public/modules.css +63 -0
- package/modules/hall-ui/public/modules.html +3 -60
- package/modules/hall-ui/public/new-project-wizard.js +7 -3
- package/modules/hall-ui/public/not-ready.html +2 -0
- package/modules/hall-ui/public/pair.css +73 -0
- package/modules/hall-ui/public/pair.html +3 -70
- package/modules/hall-ui/public/palette.js +302 -0
- package/modules/hall-ui/public/primer.css +13 -0
- package/modules/hall-ui/public/primer.html +2 -0
- package/modules/hall-ui/public/profile.html +2 -0
- package/modules/hall-ui/public/ranks.html +2 -0
- package/modules/hall-ui/public/roadmap.css +179 -0
- package/modules/hall-ui/public/roadmap.html +9 -172
- package/modules/hall-ui/public/roadmap.js +329 -316
- package/modules/hall-ui/public/sessions.css +16 -0
- package/modules/hall-ui/public/sessions.html +3 -13
- package/modules/hall-ui/public/settings.html +2 -0
- package/modules/hall-ui/public/settings.js +4 -4
- package/modules/hall-ui/public/shell.js +143 -18
- package/modules/hall-ui/public/style.css +231 -39
- package/modules/hall-ui/public/task-detail.js +347 -26
- package/modules/hall-ui/public/task.html +6 -1
- package/modules/hall-ui/public/watch.html +2 -0
- package/modules/hall-ui/public/work.html +8 -6
- package/modules/hall-ui/public/work.js +1 -1
- package/modules/ideas/inbox.js +61 -11
- package/modules/ideas/routes/inbox.js +23 -0
- package/modules/lifecycle/claim-eligibility.js +72 -0
- package/modules/lifecycle/db.js +519 -139
- package/modules/lifecycle/done-when.js +18 -3
- package/modules/lifecycle/github-push.js +19 -2
- package/modules/lifecycle/goal-advisory.js +114 -0
- package/modules/lifecycle/goal-authz.js +62 -0
- package/modules/lifecycle/lifecycle.js +9 -0
- package/modules/lifecycle/publish-reconciler.js +109 -6
- package/modules/lifecycle/routes/claims.js +68 -23
- package/modules/lifecycle/routes/done-when.js +110 -15
- package/modules/lifecycle/routes/goals.js +8 -38
- package/modules/lifecycle/routes/tasks.js +38 -6
- package/modules/lifecycle/ship-card.js +6 -0
- package/modules/onboarding/onboarding-state.js +1 -1
- package/modules/platform-identity/CLAUDE.md +15 -0
- package/modules/platform-identity/hub-device.js +147 -0
- package/modules/platform-identity/hub-keys.js +151 -0
- package/modules/platform-identity/migrations/platform_identity_001_tables.sql +114 -0
- package/modules/platform-identity/migrations/platform_identity_002_client_origin_index.sql +18 -0
- package/modules/platform-identity/migrations/platform_identity_003_project_memberships.sql +42 -0
- package/modules/platform-identity/migrations/platform_identity_004_projects.sql +44 -0
- package/modules/platform-identity/migrations/platform_identity_005_builder_activity.sql +42 -0
- package/modules/platform-identity/module.json +18 -0
- package/modules/platform-identity/platform-identity.js +531 -0
- package/modules/platform-identity/pollers/code-sweep.js +43 -0
- package/modules/platform-identity/routes/my-projects.js +140 -0
- package/modules/platform-identity/routes/projects.js +107 -0
- package/modules/platform-identity/routes/scouting.js +35 -0
- package/modules/platform-identity/routes/sso.js +402 -0
- package/modules/platform-identity/tests/platform-identity.mjs +388 -0
- package/modules/platform-identity/ui/.gitkeep +0 -0
- package/modules/security/routes/reports.js +10 -0
- package/modules/sessions/db.js +166 -21
- package/modules/sessions/module.json +1 -1
- package/modules/sessions/pollers/session-reward-reconcile.js +78 -0
- package/modules/sessions/routes/sessions.js +33 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/anim/idle/frame_00.webp +0 -0
- package/public-landing/assets/anim/idle/frame_01.webp +0 -0
- package/public-landing/assets/anim/idle/frame_02.webp +0 -0
- package/public-landing/assets/anim/idle/frame_03.webp +0 -0
- package/public-landing/assets/anim/idle/frame_04.webp +0 -0
- package/public-landing/assets/anim/idle/frame_05.webp +0 -0
- package/public-landing/assets/anim/idle/frame_06.webp +0 -0
- package/public-landing/assets/anim/idle/frame_07.webp +0 -0
- package/public-landing/assets/anim/idle/frame_08.webp +0 -0
- package/public-landing/assets/anim/idle/frame_09.webp +0 -0
- package/public-landing/assets/anim/idle/frame_10.webp +0 -0
- package/public-landing/assets/anim/idle/frame_11.webp +0 -0
- package/public-landing/assets/anim/idle/frame_12.webp +0 -0
- package/public-landing/assets/anim/idle/frame_13.webp +0 -0
- package/public-landing/assets/anim/idle/frame_14.webp +0 -0
- package/public-landing/assets/anim/idle/frame_15.webp +0 -0
- package/public-landing/assets/anim/idle/frame_16.webp +0 -0
- package/public-landing/assets/anim/idle/frame_17.webp +0 -0
- package/public-landing/assets/anim/idle/frame_18.webp +0 -0
- package/public-landing/assets/anim/idle/frame_19.webp +0 -0
- package/public-landing/assets/anim/idle/frame_20.webp +0 -0
- package/public-landing/assets/anim/idle/frame_21.webp +0 -0
- package/public-landing/assets/anim/idle/frame_22.webp +0 -0
- package/public-landing/assets/anim/idle/frame_23.webp +0 -0
- package/public-landing/assets/anim/idle/frame_24.webp +0 -0
- package/public-landing/assets/anim/idle/frame_25.webp +0 -0
- package/public-landing/assets/anim/idle/frame_26.webp +0 -0
- package/public-landing/assets/anim/idle/frame_27.webp +0 -0
- package/public-landing/assets/anim/idle/frame_28.webp +0 -0
- package/public-landing/assets/anim/idle/frame_29.webp +0 -0
- package/public-landing/assets/anim/idle/frame_30.webp +0 -0
- package/public-landing/assets/anim/idle/frame_31.webp +0 -0
- package/public-landing/assets/anim/idle/frame_32.webp +0 -0
- package/public-landing/assets/anim/idle/frame_33.webp +0 -0
- package/public-landing/assets/anim/idle/frame_34.webp +0 -0
- package/public-landing/assets/anim/idle/frame_35.webp +0 -0
- package/public-landing/assets/anim/idle/frame_36.webp +0 -0
- package/public-landing/assets/anim/idle/frame_37.webp +0 -0
- package/public-landing/assets/anim/idle/frame_38.webp +0 -0
- package/public-landing/assets/anim/idle/frame_39.webp +0 -0
- package/public-landing/assets/anim/idle/frame_40.webp +0 -0
- package/public-landing/assets/anim/idle/frame_41.webp +0 -0
- package/public-landing/assets/anim/idle/frame_42.webp +0 -0
- package/public-landing/assets/anim/idle/frame_43.webp +0 -0
- package/public-landing/create.html +51 -58
- package/public-landing/gate/README.md +137 -0
- package/public-landing/gate/dispmaps/bar.png +0 -0
- package/public-landing/gate/dispmaps/flat.png +0 -0
- package/public-landing/gate/dispmaps/key.png +0 -0
- package/public-landing/gate/index.html +587 -0
- package/public-landing/gate/previews/cloud-bongos.jpg +0 -0
- package/public-landing/gate/previews/off-the-boats.jpg +0 -0
- package/public-landing/gate/space.css +799 -0
- package/public-landing/gate/space.js +1826 -0
- package/public-landing/gate/sprites/enter.png +0 -0
- package/public-landing/gate/sprites/search.png +0 -0
- package/public-landing/gate/sprites/terminal-blank.png +0 -0
- package/public-landing/gate/sprites/tile.png +0 -0
- package/public-landing/world/assets/clouds/cloud-island-large.png +0 -0
- package/public-landing/world/assets/clouds/cloud-puff.png +0 -0
- package/public-landing/world/assets/creatures/creature-whale-cargo.png +0 -0
- package/public-landing/world/assets/creatures/creature-whale-glide.png +0 -0
- package/public-landing/world/assets/plates/plate-clouds-near.png +0 -0
- package/public-landing/world/assets/plates/scene-establishing.png +0 -0
- package/scripts/gds/abandon.js +6 -4
- package/scripts/gds/api.js +10 -5
- package/scripts/gds/api.ps1 +26 -0
- package/scripts/gds/api.sh +6 -0
- package/scripts/gds/audit-ships.js +7 -1
- package/scripts/gds/backfill-sessions.js +78 -18
- package/scripts/gds/backup.js +23 -1
- package/scripts/gds/box-connect-lib.js +124 -31
- package/scripts/gds/box-sync.js +66 -0
- package/scripts/gds/box.js +225 -53
- package/scripts/gds/cf-tunnel.js +145 -6
- package/scripts/gds/claim.js +34 -3
- package/scripts/gds/cli-lib.js +161 -27
- package/scripts/gds/code.js +6 -2
- package/scripts/gds/deploy-divergence.js +209 -0
- package/scripts/gds/dev-lib.js +309 -0
- package/scripts/gds/dev.js +322 -0
- package/scripts/gds/doctor.js +90 -61
- package/scripts/gds/fetch-art-key.js +1 -1
- package/scripts/gds/fetch-sound-prefs.js +2 -2
- package/scripts/gds/fitness.js +228 -12
- package/scripts/gds/gds-db-backup.service +16 -1
- package/scripts/gds/gds-db-offsite.service +9 -1
- package/scripts/gds/gen-api-client.js +1 -1
- package/scripts/gds/gen-api-docs.js +36 -14
- package/scripts/gds/gen-diagrams.js +45 -28
- package/scripts/gds/gen-file-map.js +2 -1
- package/scripts/gds/gen-freshness.js +32 -0
- package/scripts/gds/gen-repo-map.js +3 -2
- package/scripts/gds/gen-session-index.js +14 -5
- package/scripts/gds/hooks/pre-push.js +2 -2
- package/scripts/gds/init.js +60 -3
- package/scripts/gds/install-git-hooks.js +1 -1
- package/scripts/gds/key-card.js +15 -6
- package/scripts/gds/key-status.js +1 -1
- package/scripts/gds/linkify-refs.js +35 -9
- package/scripts/gds/login.js +16 -1
- package/scripts/gds/paste-token.js +7 -1
- package/scripts/gds/preflight.js +126 -0
- package/scripts/gds/provision.js +123 -12
- package/scripts/gds/publish-manifest.js +7 -1
- package/scripts/gds/reauth.js +7 -3
- package/scripts/gds/regen-instance-docs.js +47 -0
- package/scripts/gds/run-unit-tests.js +4 -2
- package/scripts/gds/seed-bongos-v1-seo-tasks.js +104 -0
- package/scripts/gds/seed-bv1-profiles-tasks.js +126 -0
- package/scripts/gds/sequence.js +425 -0
- package/scripts/gds/session-cost.js +10 -1
- package/scripts/gds/setup.js +31 -5
- package/scripts/gds/shell.js +5 -3
- package/scripts/gds/ship.js +319 -29
- package/scripts/gds/smoke-gds.js +139 -8
- package/scripts/gds/start.js +171 -22
- package/scripts/gds/status.js +1 -1
- package/scripts/gds/triage.js +10 -9
- package/scripts/gds/upgrade.js +124 -2
- package/scripts/status-mirror-sync.js +114 -84
- package/src/bongos/api-errors.js +1 -0
- package/src/bongos/auth.js +447 -102
- package/src/bongos/db-kernel.js +27 -0
- package/src/bongos/db.js +39 -0
- package/src/bongos/module-scope-map.js +27 -1
- package/src/bongos/permission-path-check.js +136 -102
- package/src/bongos/repo-info.js +47 -11
- package/src/bongos/route-rank-check.js +429 -46
- package/src/bongos/routes/_helpers.js +12 -1
- package/src/bongos/routes/auth.js +219 -2
- package/src/bongos/routes/backup.js +58 -2
- package/src/bongos/routes/me.js +35 -1
- package/src/bongos/serve-internal.js +84 -15
- package/src/branding.js +32 -0
- package/src/instance-config.js +30 -0
- package/src/module-api.js +112 -1
- package/src/platform-server.js +86 -9
- package/tests/abandon_task.mjs +59 -3
- package/tests/api_cli_flags.mjs +53 -0
- package/tests/auth_page_gate.mjs +13 -0
- package/tests/backfill_sessions.mjs +127 -1
- package/tests/backup_cli.mjs +54 -0
- package/tests/bongos_box_connect.mjs +77 -0
- package/tests/box_access.mjs +130 -1
- package/tests/box_credential.mjs +125 -0
- package/tests/box_host_keys.mjs +24 -0
- package/tests/box_infra_resolve.mjs +149 -0
- package/tests/box_scope_session.mjs +6 -2
- package/tests/box_sync.mjs +55 -0
- package/tests/box_task_scope.mjs +99 -0
- package/tests/branding.mjs +12 -0
- package/tests/cf_ruleset_preflight.mjs +142 -0
- package/tests/cf_tunnel.mjs +37 -0
- package/tests/claim_gate_rebase.mjs +98 -0
- package/tests/clawback_confirmed_backlog.mjs +103 -0
- package/tests/cli_lib_formatters.mjs +20 -0
- package/tests/confirm_attribution.mjs +30 -46
- package/tests/credit_grant.mjs +198 -10
- package/tests/criterion_authoring_gate.mjs +272 -0
- package/tests/criterion_progress.mjs +33 -1
- package/tests/dev_lib.mjs +253 -0
- package/tests/devbox_artifact_gate.mjs +75 -0
- package/tests/devbox_dmg_backdrop.mjs +137 -0
- package/tests/devbox_icons.mjs +304 -0
- package/tests/devbox_installer_brand.mjs +232 -0
- package/tests/devbox_key_register.mjs +234 -0
- package/tests/docs_openapi_route.mjs +110 -0
- package/tests/doctor_hooks.mjs +141 -0
- package/tests/extract_json.mjs +140 -0
- package/tests/fitness.mjs +89 -0
- package/tests/gen_freshness.mjs +96 -0
- package/tests/goal_advisory.mjs +251 -0
- package/tests/goal_criterion_confirm_ui.mjs +269 -0
- package/tests/goal_routes.mjs +59 -4
- package/tests/goal_tier.mjs +12 -3
- package/tests/governance_catalog.mjs +129 -0
- package/tests/governance_claim_eligibility.mjs +262 -0
- package/tests/governance_config.mjs +187 -0
- package/tests/governance_ownership_scope.mjs +410 -0
- package/tests/governance_page_gates.mjs +324 -0
- package/tests/governance_parity.mjs +336 -0
- package/tests/governance_principals.mjs +232 -0
- package/tests/governance_protected_surfaces.mjs +208 -0
- package/tests/governance_require_permission.mjs +223 -0
- package/tests/governance_resolver.mjs +84 -0
- package/tests/governance_role_sync.mjs +231 -0
- package/tests/governance_seed.mjs +127 -0
- package/tests/governance_session_scope.mjs +434 -0
- package/tests/governance_vanilla_boot.mjs +203 -0
- package/tests/grade_attribution.mjs +7 -7
- package/tests/hall_audit.mjs +223 -0
- package/tests/hall_nav.mjs +191 -0
- package/tests/hall_palette.mjs +206 -0
- package/tests/hall_signout.mjs +124 -0
- package/tests/hook_user_agents.mjs +10 -0
- package/tests/idea_triage.mjs +162 -0
- package/tests/idp_device_exchange.mjs +93 -0
- package/tests/init.mjs +51 -0
- package/tests/instance_config.mjs +65 -0
- package/tests/instance_mismatch_guard.mjs +23 -6
- package/tests/instance_release_config.mjs +104 -0
- package/tests/linkify_refs.mjs +15 -2
- package/tests/module_contributions.mjs +55 -0
- package/tests/module_loader.mjs +10 -5
- package/tests/module_scope_active_claims.mjs +94 -0
- package/tests/needs_rebase.mjs +92 -0
- package/tests/one_active_claim_per_session.mjs +76 -0
- package/tests/override_attribution.mjs +10 -8
- package/tests/override_request_ownership.mjs +8 -4
- package/tests/parse_cookie_decode.mjs +42 -0
- package/tests/pay_on_land.mjs +129 -0
- package/tests/platform_boot.mjs +100 -1
- package/tests/post_login_landing.mjs +42 -0
- package/tests/preflight_checks.mjs +72 -0
- package/tests/provision.mjs +132 -7
- package/tests/publish_branch_route.mjs +53 -0
- package/tests/publish_reconciler.mjs +187 -2
- package/tests/repo_info_binding.mjs +248 -0
- package/tests/reward_gate.mjs +11 -2
- package/tests/reward_mode_cost_plus_only.mjs +116 -0
- package/tests/reward_mode_surfacing.mjs +168 -0
- package/tests/route_permission_check.mjs +301 -0
- package/tests/route_rank_check.mjs +254 -12
- package/tests/route_validation.mjs +30 -0
- package/tests/sequence.mjs +267 -0
- package/tests/session_digest.mjs +44 -0
- package/tests/session_index.mjs +18 -6
- package/tests/session_records.mjs +39 -1
- package/tests/session_reward_on_land.mjs +381 -0
- package/tests/session_reward_reconcile.mjs +229 -0
- package/tests/ship_api_client_pathspec.mjs +100 -0
- package/tests/ship_base_flag.mjs +93 -0
- package/tests/ship_ci_deploy.mjs +80 -3
- package/tests/ship_upstream_deletion.mjs +57 -0
- package/tests/smoke_target.mjs +233 -0
- package/tests/start_env_guard.mjs +87 -0
- package/tests/start_rebase_warning.mjs +103 -0
- package/tests/status_mirror_sync.mjs +122 -0
- package/tests/stop_refcheck.mjs +19 -1
- package/tests/task_goal_link.mjs +15 -6
- package/tests/upgrade.mjs +139 -0
- package/tests/web_admission_status.mjs +162 -0
- package/tests/write_validation_gaps.mjs +110 -0
package/scripts/gds/fitness.js
CHANGED
|
@@ -939,8 +939,56 @@ const PROMPT_IDENTITY_LITERALS = [
|
|
|
939
939
|
/amazonprimea/i,
|
|
940
940
|
];
|
|
941
941
|
// Scan roots (recursive) + kernel-prompt CLIs that live outside them.
|
|
942
|
-
|
|
942
|
+
// scripts/gds joined at task 1002427: the builder CLI was UNSCANNED, which is how
|
|
943
|
+
// paste-token.js kept a hardcoded 'https://amazonprimea.com' long after the task
|
|
944
|
+
// 1002201 sweep replaced that literal everywhere else. The straggler surfaced as a
|
|
945
|
+
// 401 that locked a builder out of re-authenticating — a valid token verified
|
|
946
|
+
// against the wrong host — and nothing in CI could see it (idea 1000564).
|
|
947
|
+
const IDENTITY_SCAN_ROOTS = ['src/bongos', 'modules', 'scripts/gds'];
|
|
943
948
|
const IDENTITY_SCAN_EXTRA_FILES = ['scripts/gds/architect-audit.js'];
|
|
949
|
+
// PERMANENT, COUNTED carve-outs for the scripts/gds root — {file: allowed hits}.
|
|
950
|
+
//
|
|
951
|
+
// Widening the root (task 1002427) surfaced 23 files. Task 1002428 swept them:
|
|
952
|
+
// every one whose literal was a real runtime bug is FIXED (a wrong instance URL
|
|
953
|
+
// printed to a builder, an `amazonprimea-*` User-Agent that should come from
|
|
954
|
+
// branding().userAgent(), a hardcoded product name in user-facing copy). What is
|
|
955
|
+
// left below is NOT a backlog — each entry is a literal that is CORRECT as
|
|
956
|
+
// written, so this list is permanent rather than a ratchet-to-zero.
|
|
957
|
+
//
|
|
958
|
+
// The COUNT is still enforced, which is the point: an entry may not grow. A file
|
|
959
|
+
// here that gains a NEW hardcoded host fails the build exactly like an unlisted
|
|
960
|
+
// one, and any file NOT listed must have zero hits. Getting better never fails.
|
|
961
|
+
//
|
|
962
|
+
// Why each is legitimate:
|
|
963
|
+
// * box.js — REAL Cloudflare/DNS zone names for the operator plane
|
|
964
|
+
// (`dev.<host>`, the terminal zone). These are infrastructure identifiers,
|
|
965
|
+
// not an instance URL; routing them through branding would be wrong. (Its
|
|
966
|
+
// two genuine API-base defaults WERE fixed in 1002428.)
|
|
967
|
+
// * publish-manifest.js — the literal FILENAMES of two real committed docs
|
|
968
|
+
// (docs/recipes/amazonprimea-decouple-*.md). Renaming the strings without
|
|
969
|
+
// renaming the files would break the publish manifest.
|
|
970
|
+
// * backfill-sessions.js / version-close.js — one-time operator runbook text
|
|
971
|
+
// naming the actual prod DB + a historical local directory. Describing what
|
|
972
|
+
// an operator must literally type is the correct content.
|
|
973
|
+
// * seed-*.js — the hits are PROSE INSIDE seeded task descriptions: historical
|
|
974
|
+
// records of what those tasks said when they were filed. Rewriting them would
|
|
975
|
+
// falsify the record; deleting the files would destroy it. They are inert
|
|
976
|
+
// (idempotent on source_ref) and referenced only by generated manifests.
|
|
977
|
+
const IDENTITY_SCAN_ALLOWED = {
|
|
978
|
+
'scripts/gds/backfill-sessions.js': 2,
|
|
979
|
+
'scripts/gds/box.js': 3,
|
|
980
|
+
'scripts/gds/publish-manifest.js': 2,
|
|
981
|
+
'scripts/gds/seed-bongos-coreB-tranche1-tasks.js': 1,
|
|
982
|
+
'scripts/gds/seed-bongos-coreB-tranche2-tasks.js': 1,
|
|
983
|
+
'scripts/gds/seed-bongos-goal-hierarchy-tasks.js': 1,
|
|
984
|
+
'scripts/gds/seed-bongos-tasks.js': 1,
|
|
985
|
+
'scripts/gds/seed-gds-v4-tasks.js': 1,
|
|
986
|
+
'scripts/gds/seed-medusa-tasks.js': 7,
|
|
987
|
+
'scripts/gds/seed-module-upstreaming.js': 1,
|
|
988
|
+
'scripts/gds/seed-productization-tasks.js': 6,
|
|
989
|
+
'scripts/gds/seed-provisioning-tasks.js': 2,
|
|
990
|
+
'scripts/gds/version-close.js': 1,
|
|
991
|
+
};
|
|
944
992
|
const IDENTITY_SCAN_EXTS = new Set(['.js', '.html', '.css']);
|
|
945
993
|
// Path-prefix carve-outs (see the header note). Kept SMALL and documented.
|
|
946
994
|
const IDENTITY_SCAN_EXCLUDE = ['modules/game/'];
|
|
@@ -954,7 +1002,14 @@ function stripCommentsForIdentity(text, ext) {
|
|
|
954
1002
|
let t = String(text == null ? '' : text);
|
|
955
1003
|
if (ext === '.html') return t.replace(/<!--[\s\S]*?-->/g, '');
|
|
956
1004
|
t = t.replace(/\/\*[\s\S]*?\*\//g, ''); // block comments (js + css)
|
|
957
|
-
|
|
1005
|
+
// Split on /\r?\n/, NOT '\n' (task 1002428). On a CRLF checkout the old split
|
|
1006
|
+
// left a trailing \r on every line, and since `.` does not match \r the `.*$`
|
|
1007
|
+
// in the line-comment pattern could never reach end-of-string — so NO line
|
|
1008
|
+
// comment was stripped and every rationale comment naming the old brand was
|
|
1009
|
+
// scanned as if it were code. That silently broke this function's whole
|
|
1010
|
+
// contract ("a rationale comment naming the old brand never trips the scan")
|
|
1011
|
+
// on Windows, and made the check disagree with Linux CI on the same tree.
|
|
1012
|
+
if (ext === '.js') t = t.split(/\r?\n/).map((l) => l.replace(/(^|[^:])\/\/.*$/, '$1')).join('\n');
|
|
958
1013
|
return t;
|
|
959
1014
|
}
|
|
960
1015
|
|
|
@@ -1016,12 +1071,30 @@ function checkKernelPromptsNoHardcodedIdentity() {
|
|
|
1016
1071
|
for (const extra of IDENTITY_SCAN_EXTRA_FILES) {
|
|
1017
1072
|
if (!seen.has(extra)) { seen.add(extra); entries.push({ abs: path.join(CORE_ROOT, extra), rel: extra }); }
|
|
1018
1073
|
}
|
|
1074
|
+
let ratcheted = 0;
|
|
1019
1075
|
for (const { abs, rel: relFile } of entries) {
|
|
1020
1076
|
let text;
|
|
1021
1077
|
try { text = fs.readFileSync(abs, 'utf8'); }
|
|
1022
1078
|
catch { continue; } // a file moved/carved away is not a failure of this gate
|
|
1023
1079
|
scanned += 1;
|
|
1024
|
-
|
|
1080
|
+
const found = scanPromptIdentity(relFile, text);
|
|
1081
|
+
// Permanently allowed at a COUNT (task 1002428): the listed literals are
|
|
1082
|
+
// correct as written, but an entry may not GROW — a new hardcode in one of
|
|
1083
|
+
// these files fails exactly like one in an unlisted file.
|
|
1084
|
+
const allowed = IDENTITY_SCAN_ALLOWED[relFile];
|
|
1085
|
+
if (allowed !== undefined) {
|
|
1086
|
+
if (found.length > allowed) {
|
|
1087
|
+
violations.push(
|
|
1088
|
+
`${relFile} now hardcodes the host-brand identity ${found.length} time(s), above its allowed ${allowed} — ` +
|
|
1089
|
+
`the carve-out covers the EXISTING literals (see IDENTITY_SCAN_ALLOWED), not new ones. ` +
|
|
1090
|
+
`Read the host from branding()/window.__BRANDING__ instead (task 1002428).`
|
|
1091
|
+
);
|
|
1092
|
+
} else {
|
|
1093
|
+
ratcheted += 1;
|
|
1094
|
+
}
|
|
1095
|
+
continue;
|
|
1096
|
+
}
|
|
1097
|
+
violations.push(...found);
|
|
1025
1098
|
}
|
|
1026
1099
|
return {
|
|
1027
1100
|
name: 'portable code carries no hardcoded host identity (BV1.R76 / task 1705)',
|
|
@@ -1029,7 +1102,43 @@ function checkKernelPromptsNoHardcodedIdentity() {
|
|
|
1029
1102
|
hardFail: violations.length > 0,
|
|
1030
1103
|
violations,
|
|
1031
1104
|
warnings: [],
|
|
1032
|
-
note: `${scanned} portable source file(s) scanned (src/bongos/** + modules/** + kernel-prompt CLIs, minus the instance-1 product + generated dirs) — none may hardcode the host brand; identity comes from branding()/window.__BRANDING__ (ADR 0062 §3/§5).`,
|
|
1105
|
+
note: `${scanned} portable source file(s) scanned (src/bongos/** + modules/** + scripts/gds/** + kernel-prompt CLIs, minus the instance-1 product + generated dirs) — none may hardcode the host brand; identity comes from branding()/window.__BRANDING__ (ADR 0062 §3/§5). ${ratcheted} scripts/gds file(s) hold documented permanent carve-outs (real DNS zones, real doc filenames, operator runbook text, historical seeded prose) capped at a count they may not exceed — task 1002428.`,
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// ============================================================================
|
|
1110
|
+
// Check 15b — core-shipped NEUTRAL config packs carry no host identity (BV1.R102)
|
|
1111
|
+
// ============================================================================
|
|
1112
|
+
//
|
|
1113
|
+
// Sibling of Check 15 for the CONFIG class. IDENTITY_SCAN_EXTS is js/html/css, so
|
|
1114
|
+
// the scan above never opens a .json — yet config/*.neutral.json is precisely what
|
|
1115
|
+
// a vanilla instance boots from (branding R76, governance R96). A host name leaking
|
|
1116
|
+
// into a neutral pack ships one instance's brand to EVERY fresh instance, and until
|
|
1117
|
+
// this check nothing in CI could see it.
|
|
1118
|
+
//
|
|
1119
|
+
// Only the NEUTRAL packs are scanned, deliberately: an instance pack (config/
|
|
1120
|
+
// branding.json, config/governance.json) is host content that legitimately names
|
|
1121
|
+
// its host, and is boundary-excluded from the published core anyway.
|
|
1122
|
+
function checkNeutralConfigsNoHostIdentity() {
|
|
1123
|
+
const violations = [];
|
|
1124
|
+
const dir = path.join(CORE_ROOT, 'config');
|
|
1125
|
+
let names = [];
|
|
1126
|
+
try { names = fs.readdirSync(dir).filter((n) => n.endsWith('.neutral.json')).sort(); }
|
|
1127
|
+
catch { /* no config/ dir in this root — nothing to gate */ }
|
|
1128
|
+
for (const name of names) {
|
|
1129
|
+
const relFile = `config/${name}`;
|
|
1130
|
+
let text;
|
|
1131
|
+
try { text = fs.readFileSync(path.join(dir, name), 'utf8'); }
|
|
1132
|
+
catch { continue; } // a pack that moved away is not a failure of this gate
|
|
1133
|
+
violations.push(...scanPromptIdentity(relFile, text, '.json'));
|
|
1134
|
+
}
|
|
1135
|
+
return {
|
|
1136
|
+
name: 'neutral config packs carry no host identity (BV1.R102)',
|
|
1137
|
+
ok: violations.length === 0,
|
|
1138
|
+
hardFail: violations.length > 0,
|
|
1139
|
+
violations,
|
|
1140
|
+
warnings: [],
|
|
1141
|
+
note: `${names.length} core-shipped config/*.neutral.json pack(s) scanned — a vanilla instance boots from these alone, so none may name a host brand. Instance packs (config/<name>.json) are host content and are deliberately NOT scanned.`,
|
|
1033
1142
|
};
|
|
1034
1143
|
}
|
|
1035
1144
|
|
|
@@ -1076,7 +1185,77 @@ function checkDesignContract() {
|
|
|
1076
1185
|
// reskin/module-toggle reaches every widget). Self-contained: a pure scan of
|
|
1077
1186
|
// index.html's <main>, no module/registry state needed. Exported for the unit test.
|
|
1078
1187
|
// (The hall surface itself carved to modules/hall-ui/public/ in BV1.R85.)
|
|
1188
|
+
//
|
|
1189
|
+
// task 2177 — the check also asserts the HOME ZONE BUDGETS. builders.js caps how
|
|
1190
|
+
// many widgets each Home zone mounts (ZONE_BUDGETS); past the cap a widget still
|
|
1191
|
+
// mounts, but into a hidden overflow behind a "More" link. That graceful degrade
|
|
1192
|
+
// is the runtime safety net — it is NOT a licence to keep adding cards, and
|
|
1193
|
+
// because nothing goes red, a Home that has quietly outgrown its budget looks
|
|
1194
|
+
// fine. So CI counts the registrations statically and fails on a breach: the
|
|
1195
|
+
// budget becomes a decision someone has to make deliberately (raise the number
|
|
1196
|
+
// in builders.js, with a reason) instead of one that erodes.
|
|
1197
|
+
//
|
|
1198
|
+
// NOT statically checkable, by design: the `home-banner` rule is "at most ONE
|
|
1199
|
+
// VISIBLE", not "at most one registered" — both banner widgets (welcome,
|
|
1200
|
+
// getting-started) are hiddenByDefault and reveal themselves from onboarding
|
|
1201
|
+
// state, so only runtime knows the count. It is documented as a runtime
|
|
1202
|
+
// invariant in modules/hall-ui/CLAUDE.md rather than faked with a static count
|
|
1203
|
+
// that would either fail today or assert nothing.
|
|
1079
1204
|
const HALL_INDEX = path.join(moduleDir('hall-ui'), 'public', 'index.html'); // hall-ui is a module — resolve its root (ADR 0108)
|
|
1205
|
+
const HALL_PUBLIC = path.join(moduleDir('hall-ui'), 'public');
|
|
1206
|
+
const HALL_BUILDERS_JS = path.join(HALL_PUBLIC, 'builders.js');
|
|
1207
|
+
|
|
1208
|
+
// Parse builders.js's ZONE_BUDGETS object literal — the single source of truth
|
|
1209
|
+
// for the caps (this check must never carry its own copy of the numbers).
|
|
1210
|
+
function parseZoneBudgets(js) {
|
|
1211
|
+
const block = js.match(/const\s+ZONE_BUDGETS\s*=\s*\{([^}]*)\}/);
|
|
1212
|
+
if (!block) return null;
|
|
1213
|
+
const budgets = {};
|
|
1214
|
+
for (const [, zone, n] of block[1].matchAll(/['"]([\w-]+)['"]\s*:\s*(\d+)/g)) {
|
|
1215
|
+
budgets[zone] = Number(n);
|
|
1216
|
+
}
|
|
1217
|
+
return Object.keys(budgets).length ? budgets : null;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
// Count registrations per Home zone across every hall client script that calls
|
|
1221
|
+
// contributeHallWidget. The zones are static string literals in the registration
|
|
1222
|
+
// object, so a literal scan is exact for the code we have (and a computed zone
|
|
1223
|
+
// would be a placement smell in its own right — dom-utils' runtime validation
|
|
1224
|
+
// still rejects an unknown one). Requiring the contributeHallWidget mention is
|
|
1225
|
+
// what keeps an unrelated `zone:` key in some other object from eating budget.
|
|
1226
|
+
function scanHallZoneCounts(dir = HALL_PUBLIC, fsImpl = fs) {
|
|
1227
|
+
const counts = {};
|
|
1228
|
+
let files = [];
|
|
1229
|
+
try { files = fsImpl.readdirSync(dir).filter((f) => f.endsWith('.js')); } catch { return counts; }
|
|
1230
|
+
for (const file of files) {
|
|
1231
|
+
let src = '';
|
|
1232
|
+
try { src = fsImpl.readFileSync(path.join(dir, file), 'utf8'); } catch { continue; }
|
|
1233
|
+
if (!src.includes('contributeHallWidget')) continue;
|
|
1234
|
+
for (const [, zone] of src.matchAll(/\bzone:\s*['"]([\w-]+)['"]/g)) {
|
|
1235
|
+
counts[zone] = (counts[zone] || 0) + 1;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
return counts;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
function checkHallZoneBudgets(fsImpl = fs) {
|
|
1242
|
+
let js = '';
|
|
1243
|
+
try { js = fsImpl.readFileSync(HALL_BUILDERS_JS, 'utf8'); } catch { return []; }
|
|
1244
|
+
const budgets = parseZoneBudgets(js);
|
|
1245
|
+
if (!budgets) {
|
|
1246
|
+
return ['modules/hall-ui/public/builders.js no longer declares a parseable ZONE_BUDGETS literal — the Home zone budgets are unenforced (task 2177).'];
|
|
1247
|
+
}
|
|
1248
|
+
const counts = scanHallZoneCounts(HALL_PUBLIC, fsImpl);
|
|
1249
|
+
const violations = [];
|
|
1250
|
+
for (const [zone, budget] of Object.entries(budgets)) {
|
|
1251
|
+
const n = counts[zone] || 0;
|
|
1252
|
+
if (n > budget) {
|
|
1253
|
+
violations.push(
|
|
1254
|
+
`Home zone "${zone}" has ${n} registered hall widgets but its budget is ${budget} — the extras mount into the hidden overflow, so Home silently grew. Move it to its own page (see modules/hall-ui/CLAUDE.md §Placement), or raise ZONE_BUDGETS['${zone}'] in modules/hall-ui/public/builders.js deliberately (task 2177).`);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
return violations;
|
|
1258
|
+
}
|
|
1080
1259
|
|
|
1081
1260
|
function hallMainSlice(html) {
|
|
1082
1261
|
// Strip HTML comments first so a comment that merely MENTIONS <main> or a
|
|
@@ -1107,13 +1286,14 @@ function checkHallSectionRegistry() {
|
|
|
1107
1286
|
const hardcoded = scanHallSectionRegistry(html);
|
|
1108
1287
|
const violations = hardcoded.map((s) =>
|
|
1109
1288
|
`modules/hall-ui/public/index.html <main> hardcodes a hall section "${s}" — register it via window.OTB.contributeHallWidget (modules/hall-ui/public/hall-widgets.js for core, a module's client script otherwise), don't inline the markup (BV1.R75 / ADR 0062 §4).`);
|
|
1289
|
+
violations.push(...checkHallZoneBudgets());
|
|
1110
1290
|
return {
|
|
1111
|
-
name: 'hall widgets are registry-owned (ADR 0062 §4)',
|
|
1291
|
+
name: 'hall widgets are registry-owned + within zone budget (ADR 0062 §4, task 2177)',
|
|
1112
1292
|
ok: violations.length === 0,
|
|
1113
1293
|
hardFail: violations.length > 0,
|
|
1114
1294
|
violations,
|
|
1115
1295
|
warnings: [],
|
|
1116
|
-
note: 'modules/hall-ui/public/index.html <main> contains no hardcoded <section class="scroll"
|
|
1296
|
+
note: 'modules/hall-ui/public/index.html <main> contains no hardcoded <section class="scroll">, and every Home zone is within the ZONE_BUDGETS cap builders.js declares — the registry is the single source of truth and Home cannot grow unnoticed.',
|
|
1117
1297
|
};
|
|
1118
1298
|
}
|
|
1119
1299
|
|
|
@@ -1126,8 +1306,9 @@ function checkHallSectionRegistry() {
|
|
|
1126
1306
|
// config/modules.json, …) is boundary-EXCLUDED, so a bare `require('../../config/<excluded>.json')`
|
|
1127
1307
|
// in core code throws MODULE_NOT_FOUND and CRASHES the boot on a fresh instance — the exact
|
|
1128
1308
|
// class that shipped in R88 (two hard-requires in src/bongos/serve-internal.js; task 1824). The
|
|
1129
|
-
// cure is to
|
|
1130
|
-
//
|
|
1309
|
+
// cure is to READ the config instead of requiring it, tolerating a missing file
|
|
1310
|
+
// (tryLoadInstanceConfig in src/bongos/serve-internal.js / fs.existsSync → fail-to-vanilla), which
|
|
1311
|
+
// also drops the require() literal so it no longer matches here. Check 1 (core↔host) scans
|
|
1131
1312
|
// code→code IMPORTS and misses this code→config class. Only an EXCLUDED config is a violation;
|
|
1132
1313
|
// a publishable neutral.*/schema config (which ships with the core) is fine.
|
|
1133
1314
|
const CONFIG_REQUIRE_RE = /require\(\s*['"]([^'"]*config\/[^'"]*\.json)['"]\s*\)/g;
|
|
@@ -1155,7 +1336,7 @@ function checkCoreRequiresExcludedConfig() {
|
|
|
1155
1336
|
while ((m = CONFIG_REQUIRE_RE.exec(raw))) {
|
|
1156
1337
|
const relCfg = rel(path.resolve(path.dirname(abs), m[1]));
|
|
1157
1338
|
if (!isPublishable(relCfg)) {
|
|
1158
|
-
violations.push(`${relFile} → require('${m[1]}') hard-requires an instance-excluded config (${relCfg}) — guard the read (
|
|
1339
|
+
violations.push(`${relFile} → require('${m[1]}') hard-requires an instance-excluded config (${relCfg}) — guard the read (tryLoadInstanceConfig in src/bongos/serve-internal.js / fs.existsSync, fail-to-vanilla) so a fresh core boots (task 1824).`);
|
|
1159
1340
|
}
|
|
1160
1341
|
}
|
|
1161
1342
|
}
|
|
@@ -1362,15 +1543,45 @@ const WRITE_VALIDATION_EXEMPT = new Set([
|
|
|
1362
1543
|
'PATCH /me/display-name', // { display_name } — displayName.validate
|
|
1363
1544
|
]);
|
|
1364
1545
|
|
|
1546
|
+
// KNOWN GAPS, NOT EXEMPTIONS (task 1002551 / BV1.R109b → tracked by task 1002552).
|
|
1547
|
+
//
|
|
1548
|
+
// These three are NOT validated-by-lib and do NOT belong in WRITE_VALIDATION_EXEMPT
|
|
1549
|
+
// above — they are genuine ADR 0118 gaps that must grow a validateOrRespond schema.
|
|
1550
|
+
// They surface here only because R109b taught route discovery to see MULTI-LINE
|
|
1551
|
+
// `router.<method>(` declarations: this check reuses gen-api-docs.extractModel(), so
|
|
1552
|
+
// the same regex hole that hid them from the rank auditor hid them from Check 18 too.
|
|
1553
|
+
// They are pre-existing (never validated), not a regression.
|
|
1554
|
+
//
|
|
1555
|
+
// They are recorded rather than fixed because modules/*/routes/*.js was being migrated
|
|
1556
|
+
// concurrently by BV1.R105, so editing those files would have collided. This is the
|
|
1557
|
+
// Check-17 ratchet posture: a NAMED, non-growing baseline. The list may only ever
|
|
1558
|
+
// SHRINK — a new unvalidated write route is not on it and still hard-fails the build,
|
|
1559
|
+
// and a stale entry (one that HAS been fixed) also fails, so the list cannot rot.
|
|
1560
|
+
// Delete this constant when task 1002552 lands and Check 18 returns to hard-zero.
|
|
1561
|
+
// EMPTY as of BV1.R109c (task 1002552) — all three original entries now validate.
|
|
1562
|
+
// Kept (rather than deleted) as the ratchet itself: an empty named baseline states
|
|
1563
|
+
// the invariant is hard-zero, and any future entry has to be added deliberately.
|
|
1564
|
+
const WRITE_VALIDATION_BASELINE = new Set([]);
|
|
1565
|
+
|
|
1365
1566
|
function scanUnvalidatedWriteRoutes(model = genApiDocs.extractModel()) {
|
|
1366
1567
|
const unvalidated = [];
|
|
1568
|
+
const baselineSeen = new Set();
|
|
1367
1569
|
for (const r of model) {
|
|
1368
1570
|
if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(r.method)) continue;
|
|
1369
1571
|
// Only routes that actually read a body have an input surface to validate;
|
|
1370
1572
|
// a write that ignores req.body has nothing to declare.
|
|
1371
1573
|
if (!r.body || !r.body.readsBody || r.body.validated) continue;
|
|
1372
|
-
|
|
1373
|
-
|
|
1574
|
+
const key = `${r.method} ${r.route}`;
|
|
1575
|
+
if (WRITE_VALIDATION_EXEMPT.has(key)) continue;
|
|
1576
|
+
if (WRITE_VALIDATION_BASELINE.has(key)) { baselineSeen.add(key); continue; }
|
|
1577
|
+
unvalidated.push(`${key} [${r.file}]`);
|
|
1578
|
+
}
|
|
1579
|
+
// A baseline entry that no longer reports as unvalidated has been FIXED — it must be
|
|
1580
|
+
// removed, or the list would quietly license a future regression on that same route.
|
|
1581
|
+
for (const key of WRITE_VALIDATION_BASELINE) {
|
|
1582
|
+
if (!baselineSeen.has(key)) {
|
|
1583
|
+
unvalidated.push(`${key} [STALE BASELINE — this route now validates; remove it from WRITE_VALIDATION_BASELINE in scripts/gds/fitness.js]`);
|
|
1584
|
+
}
|
|
1374
1585
|
}
|
|
1375
1586
|
return unvalidated;
|
|
1376
1587
|
}
|
|
@@ -1389,7 +1600,7 @@ function checkWriteRoutesValidated() {
|
|
|
1389
1600
|
hardFail: violations.length > 0,
|
|
1390
1601
|
violations,
|
|
1391
1602
|
warnings: [],
|
|
1392
|
-
note: `body-reading POST/PUT/PATCH/DELETE routes must call validateOrRespond/validate (${WRITE_VALIDATION_EXEMPT.size} dynamic-key prefs routes validate by lib); ${unvalidated.length} unvalidated.`,
|
|
1603
|
+
note: `body-reading POST/PUT/PATCH/DELETE routes must call validateOrRespond/validate (${WRITE_VALIDATION_EXEMPT.size} dynamic-key prefs routes validate by lib; ${WRITE_VALIDATION_BASELINE.size} known gaps tracked by task 1002552, list may only shrink); ${unvalidated.length} unvalidated.`,
|
|
1393
1604
|
};
|
|
1394
1605
|
}
|
|
1395
1606
|
|
|
@@ -1411,6 +1622,7 @@ const CHECKS = [
|
|
|
1411
1622
|
checkMigrationNamespacing, // Check 11b — ADR 0108 §5 cross-repo migration filenames + stem collision
|
|
1412
1623
|
checkDesignContract,
|
|
1413
1624
|
checkKernelPromptsNoHardcodedIdentity, // BV1.R76 — kernel prompts read identity from branding()
|
|
1625
|
+
checkNeutralConfigsNoHostIdentity, // Check 15b — BV1.R102: .json is outside the scan above
|
|
1414
1626
|
checkHallSectionRegistry,
|
|
1415
1627
|
checkCoreRequiresExcludedConfig, // task 1824 — core→instance-config require guard
|
|
1416
1628
|
checkCoreRequiresExcludedModule, // Check 16b — task 2135: core→excluded-module require guard (R89 split)
|
|
@@ -1463,9 +1675,13 @@ module.exports = {
|
|
|
1463
1675
|
checkKernelPromptsNoHardcodedIdentity, // BV1.R76 / task 1705
|
|
1464
1676
|
scanPromptIdentity, // BV1.R76 — negative-test hook
|
|
1465
1677
|
stripCommentsForIdentity, // task 1705 — comment-strip helper (tested)
|
|
1678
|
+
checkNeutralConfigsNoHostIdentity, // Check 15b — BV1.R102 (tested)
|
|
1466
1679
|
checkHallSectionRegistry,
|
|
1467
1680
|
scanHallSectionRegistry,
|
|
1468
1681
|
hallMainSlice,
|
|
1682
|
+
parseZoneBudgets, // task 2177 — hall Home zone budgets (tested)
|
|
1683
|
+
scanHallZoneCounts,
|
|
1684
|
+
checkHallZoneBudgets,
|
|
1469
1685
|
checkErrorEnvelope, // Check 17 — BV1 R03 / task 1990 (ADR 0116)
|
|
1470
1686
|
scanFlatErrorSites, // shared with the R04 sweep + its test
|
|
1471
1687
|
countFlatErrors, // per-text detector (tested)
|
|
@@ -10,7 +10,22 @@ User=lars
|
|
|
10
10
|
Group=lars
|
|
11
11
|
# Run the canonical script from the deployed repo. Keep this path stable;
|
|
12
12
|
# the recipe doc points at it for restore + manual-one-shot use cases.
|
|
13
|
-
|
|
13
|
+
#
|
|
14
|
+
# Invoke through `bash` explicitly, NOT as a bare ExecStart path. Every .sh in
|
|
15
|
+
# this repo is committed mode 100644 (they are run as `bash script.sh` — the
|
|
16
|
+
# /backup/trigger route does exactly that via execFile('bash', …)), so a bare
|
|
17
|
+
# ExecStart fails `203/EXEC` the moment the file lands without an exec bit.
|
|
18
|
+
# That is not hypothetical: on cloudbongos this unit died silently for 13 days
|
|
19
|
+
# (2026-07-15 → 07-28) because a core upgrade re-extracted the script 0664, and
|
|
20
|
+
# a `chmod +x` fix does not survive the NEXT upgrade. Going through bash makes
|
|
21
|
+
# the exec bit irrelevant (task 1002479).
|
|
22
|
+
#
|
|
23
|
+
# EDIT THIS PATH when installing — it must point at the deployed core scripts
|
|
24
|
+
# dir for THIS instance, which differs by layout:
|
|
25
|
+
# vendored/standalone: /home/<user>/<instance>/node_modules/@bongos/core/scripts/gds/db-backup-nightly.sh
|
|
26
|
+
# full clone: /home/<user>/<repo>/scripts/gds/db-backup-nightly.sh
|
|
27
|
+
# See docs/recipes/gds-db-backup.md.
|
|
28
|
+
ExecStart=/usr/bin/env bash /home/lars/amazonprimea/scripts/gds/db-backup-nightly.sh
|
|
14
29
|
# Modest hardening — the script only needs read on the socket + write on
|
|
15
30
|
# /var/backups/gds. Anything else is gravy for forensics.
|
|
16
31
|
NoNewPrivileges=true
|
|
@@ -10,7 +10,15 @@ User=lars
|
|
|
10
10
|
Group=lars
|
|
11
11
|
# Run the canonical script from the deployed repo. Keep this path stable;
|
|
12
12
|
# the recipe doc points at it for restore + manual-one-shot use cases.
|
|
13
|
-
|
|
13
|
+
#
|
|
14
|
+
# Invoke through `bash`, NOT as a bare ExecStart path — see the long note in
|
|
15
|
+
# gds-db-backup.service. Same trap, same fix: the .sh files here are committed
|
|
16
|
+
# mode 100644, so a bare ExecStart dies `203/EXEC` and no chmod survives a core
|
|
17
|
+
# upgrade. The nightly unit lost 13 days of backups to exactly this (1002479);
|
|
18
|
+
# this unit would have hit it on its first run.
|
|
19
|
+
#
|
|
20
|
+
# EDIT THIS PATH when installing (layout differs — vendored vs full clone).
|
|
21
|
+
ExecStart=/usr/bin/env bash /home/lars/amazonprimea/scripts/gds/db-backup-offsite.sh
|
|
14
22
|
# S3 credentials for the Space live outside the repo (ADR 0022). systemd
|
|
15
23
|
# injects them; the file is chmod 600, owned by lars. Missing-file is
|
|
16
24
|
# tolerated here (the script fails loudly if the vars are unset) so a
|
|
@@ -439,7 +439,7 @@ function renderReadme(spec, ops) {
|
|
|
439
439
|
`import { createClient, ApiError } from '@bongos/client';`,
|
|
440
440
|
``,
|
|
441
441
|
`const api = createClient({`,
|
|
442
|
-
` baseUrl: 'https://
|
|
442
|
+
` baseUrl: 'https://example.com${base}', // your instance origin — or omit for same-origin (browser)`,
|
|
443
443
|
` token: process.env.BONGOS_TOKEN, // Authorization: Bearer <token>`,
|
|
444
444
|
`});`,
|
|
445
445
|
``,
|
|
@@ -41,6 +41,7 @@ const { API_PREFIX, API_VERSION, VERSIONED_API_PREFIX } = require('../../src/bon
|
|
|
41
41
|
// category taxonomy the spec advertises (ADR 0116). Keep them pure — the generator
|
|
42
42
|
// must never execute a route file or open a connection.
|
|
43
43
|
const { LIMITS } = require('../../src/bongos/routes/_helpers');
|
|
44
|
+
const { sameContent } = require('./gen-freshness'); // eol-insensitive freshness (CRLF checkouts)
|
|
44
45
|
const apiErrors = require('../../src/bongos/api-errors');
|
|
45
46
|
|
|
46
47
|
const CORE_ROOT = resolveCoreRoot();
|
|
@@ -451,6 +452,11 @@ function extractBody(lines, i, method, constants = {}) {
|
|
|
451
452
|
function responseValueSchema(expr) {
|
|
452
453
|
const v = String(expr).trim();
|
|
453
454
|
if (v === 'true' || v === 'false') return { type: 'boolean' };
|
|
455
|
+
// `!!x` and `Boolean(x)` are boolean whatever x is — the two idioms routes use
|
|
456
|
+
// to coerce a truthy value before returning it (task 1002430). Without this a
|
|
457
|
+
// handler returning `{ ok: true }` in one branch and `{ ok: !!v }` in another
|
|
458
|
+
// documents `ok` as untyped, which is strictly worse than the truth.
|
|
459
|
+
if (/^!!/.test(v) || /^Boolean\s*\(/.test(v)) return { type: 'boolean' };
|
|
454
460
|
if (/^pageMeta\s*\(/.test(v)) return { $ref: '#/components/schemas/PageInfo' };
|
|
455
461
|
return {}; // a variable / call / ternary — the KEY is the win; leave the value open
|
|
456
462
|
}
|
|
@@ -533,26 +539,38 @@ function tagFor(route) {
|
|
|
533
539
|
|
|
534
540
|
// The full extracted model: [{ method, route, openapiPath, params, rank, tag,
|
|
535
541
|
// description, body, file }], stable-sorted.
|
|
536
|
-
|
|
542
|
+
// `routeFiles` is injectable so a test can drive the model over a fixture route file
|
|
543
|
+
// (the default is the live core + module route set).
|
|
544
|
+
function extractModel({ routeFiles = rrc.discoverRouteFiles() } = {}) {
|
|
537
545
|
const routes = [];
|
|
538
546
|
const globals = globalConstants();
|
|
539
|
-
for (const { abs, rel } of
|
|
547
|
+
for (const { abs, rel } of routeFiles) {
|
|
540
548
|
const text = fs.readFileSync(abs, 'utf8');
|
|
541
549
|
const lines = text.split('\n');
|
|
542
550
|
// Constants a validate() rule in THIS file may reference (LIMITS + file-local consts).
|
|
543
551
|
const constants = Object.assign({}, globals, scanFileConstants(text));
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
552
|
+
// rrc.scanDeclarations, not a second regex: the spec must enumerate EXACTLY the
|
|
553
|
+
// routes the ship-time gate enforces. Its own regex missed multi-line declarations,
|
|
554
|
+
// so 10 real endpoints (6 writes) were absent from the published contract entirely
|
|
555
|
+
// (BV1.R109b). An unresolvable path is skipped here and reported by checkRouteRanks.
|
|
556
|
+
for (const decl of rrc.scanDeclarations(text)) {
|
|
557
|
+
if (decl.route === null) continue;
|
|
558
|
+
const i = decl.line;
|
|
559
|
+
const method = decl.method;
|
|
560
|
+
const route = decl.route;
|
|
549
561
|
const { openapiPath, names } = pathParams(route);
|
|
562
|
+
// classifyGate, not classifyRank: a route migrated to requirePermission(...)
|
|
563
|
+
// (BV1.R104-R107) resolves through the governance catalog to its real rank
|
|
564
|
+
// floor, and names the authority atoms it gates on. Before BV1.R109 those
|
|
565
|
+
// routes documented as 'any-builder' — enforcement was correct, the LABEL lied.
|
|
566
|
+
const { rank, perms } = rrc.classifyGate(lines, i);
|
|
550
567
|
routes.push({
|
|
551
568
|
method,
|
|
552
569
|
route,
|
|
553
570
|
openapiPath,
|
|
554
571
|
params: names,
|
|
555
|
-
rank
|
|
572
|
+
rank,
|
|
573
|
+
perms,
|
|
556
574
|
tag: tagFor(route),
|
|
557
575
|
description: commentBlockAbove(lines, i),
|
|
558
576
|
body: extractBody(lines, i, method, constants),
|
|
@@ -697,10 +715,14 @@ function buildOperation(r, stats, schemas) {
|
|
|
697
715
|
tags: [r.tag],
|
|
698
716
|
summary: `${r.method} ${r.route}`,
|
|
699
717
|
description: (r.description ? r.description + '\n\n' : '')
|
|
700
|
-
+ `**Rank:** \`${r.rank}\` — ${RANK_BLURB[r.rank] || RANK_BLURB.unknown}
|
|
718
|
+
+ `**Rank:** \`${r.rank}\` — ${RANK_BLURB[r.rank] || RANK_BLURB.unknown}`
|
|
719
|
+
+ (r.perms && r.perms.length ? `\n\n**Permissions:** \`${r.perms.join('`, `')}\` (all required).` : ''),
|
|
701
720
|
'x-rank': r.rank,
|
|
702
721
|
'x-source': r.file,
|
|
703
722
|
};
|
|
723
|
+
// The authority ATOMS a migrated route gates on (ADR 0151). Absent on a route still
|
|
724
|
+
// gated by rank, so today's spec is byte-identical until BV1.R104-R107 land.
|
|
725
|
+
if (r.perms && r.perms.length) op['x-permissions'] = r.perms;
|
|
704
726
|
if (r.params.length) {
|
|
705
727
|
op.parameters = r.params.map((name) => ({
|
|
706
728
|
name, in: 'path', required: true, schema: { type: 'string' },
|
|
@@ -768,11 +790,11 @@ function buildOpenapi(model) {
|
|
|
768
790
|
const spec = {
|
|
769
791
|
openapi: '3.1.0',
|
|
770
792
|
info: {
|
|
771
|
-
title: `${productName()} —
|
|
793
|
+
title: `${productName()} — Bongos API`,
|
|
772
794
|
version: specVersion(),
|
|
773
795
|
license: { name: 'AGPL-3.0-or-later', url: 'https://www.gnu.org/licenses/agpl-3.0.html' },
|
|
774
796
|
description: [
|
|
775
|
-
`The
|
|
797
|
+
`The Bongos API — the build-methodology backend`,
|
|
776
798
|
`(builders, tasks, claims, credits, grading, memory, dev boxes).`,
|
|
777
799
|
``,
|
|
778
800
|
`**API version:** \`${API_VERSION}\` — served at \`${VERSIONED_API_PREFIX}\`. The`,
|
|
@@ -843,7 +865,7 @@ function buildReferenceMd(model, spec) {
|
|
|
843
865
|
const L = [];
|
|
844
866
|
L.push(MD_BANNER);
|
|
845
867
|
L.push('');
|
|
846
|
-
L.push('#
|
|
868
|
+
L.push('# Bongos API reference');
|
|
847
869
|
L.push('');
|
|
848
870
|
L.push(`> **Generated from the live route files** — the route file is authoritative. ${model.length} endpoints across ${new Set(model.map((r) => r.file)).size} route files.`);
|
|
849
871
|
L.push('> Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).');
|
|
@@ -986,8 +1008,8 @@ function main() {
|
|
|
986
1008
|
if (checkOnly) {
|
|
987
1009
|
const stale = files.filter((f) => {
|
|
988
1010
|
let cur = null;
|
|
989
|
-
try { cur = fs.readFileSync(f.full, 'utf8'); } catch { /* missing → stale */ }
|
|
990
|
-
return cur
|
|
1011
|
+
try { cur = fs.readFileSync(f.full, 'utf8'); } catch { return true; /* missing → stale */ }
|
|
1012
|
+
return !sameContent(cur, f.content);
|
|
991
1013
|
});
|
|
992
1014
|
if (stale.length) {
|
|
993
1015
|
console.error(`gen-api-docs --check: STALE — would update ${stale.map((f) => f.rel).join(', ')}. Run \`node scripts/gds/gen-api-docs.js\` and commit.`);
|