@bongos/core 1.19.1073 → 1.19.1075
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 +404 -549
- package/.claude/skills/feedback/SKILL.md +2 -2
- package/bin/bongos.js +1 -3
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -60
- package/clients/bongos-client/index.cjs +4 -60
- package/clients/bongos-client/index.d.ts +5 -90
- package/clients/bongos-client/index.mjs +4 -60
- package/config/branding.neutral.json +1 -5
- package/config/modules.neutral.json +2 -3
- package/config/scheduled-routines.json +0 -2
- package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +1 -0
- package/docs/adr/0285-a-shared-box-holds-about-twelve-projects-per-gb-and-memory-is-the-wall.md +1 -0
- package/docs/adr/0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md +1 -1
- package/docs/adr/0327-a-cloud-host-runs-on-the-owners-account-and-the-key-is-borrowed.md +1 -1
- package/docs/adr/0341-the-page-is-the-unit-of-tweak-mode.md +2 -0
- package/docs/adr/0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md +94 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +175 -1665
- package/docs/api-reference.md +9 -41
- package/docs/architecture.md +16 -1
- package/docs/branding-contract.md +0 -3
- package/docs/copy-inventory.md +454 -479
- package/docs/copy-registry.json +746 -978
- package/docs/file-map.md +5 -34
- package/docs/module-api-changelog.md +4 -0
- package/docs/modules-contract.md +13 -1
- package/docs/onboarding/diagrams/04-architecture.mmd +11 -22
- package/docs/onboarding/diagrams/README.md +3 -3
- package/docs/onboarding/diagrams/assertions.json +2 -22
- package/docs/onboarding/primer.md +9 -28
- package/docs/page-inventory.json +4 -1
- package/docs/page-readings.json +1032 -1077
- package/docs/recipes/render-pilot.md +29 -10
- package/migrations/core_257_module_store_registry.sql +87 -0
- package/migrations/core_258_drop_dev_box_tables.sql +55 -0
- package/modules/agents/module.json +1 -0
- package/modules/autonomy/module.json +1 -0
- package/modules/builder-settings/module.json +1 -0
- package/modules/builder-settings/render-prefs.js +4 -5
- package/modules/copy-desk/docx.js +78 -5
- package/modules/copy-desk/module.json +1 -0
- package/modules/copy-desk/routes/copy-desk.js +10 -10
- package/modules/copy-desk/tests/copy_docx.mjs +107 -1
- package/modules/copy-desk/tests/fixtures/word-docx.mjs +30 -0
- package/modules/discord/module.json +1 -0
- package/modules/discord/ship-broadcast.js +3 -3
- package/modules/economy/credits.js +2 -2
- package/modules/economy/module.json +1 -0
- package/modules/economy/routes/credits.js +1 -2
- package/modules/government/catalog.js +13 -17
- package/modules/government/migrations/government_019_retire_box_permissions.sql +33 -0
- package/modules/government/module.json +1 -0
- package/modules/government/protected-surfaces.json +3 -11
- package/modules/government/resolver.js +3 -46
- package/modules/government/routes/government.js +0 -2
- package/modules/government/session-scopes.js +43 -83
- package/modules/government/session-scopes.json +8 -18
- package/modules/grading/module.json +1 -0
- package/modules/hall-ui/module.json +1 -0
- package/modules/hall-ui/public/board-lib.js +2 -2
- package/modules/hall-ui/public/builders.js +29 -40
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/collab.js +1 -1
- package/modules/hall-ui/public/diagrams.html +3 -3
- package/modules/hall-ui/public/dom-utils.js +1 -1
- package/modules/hall-ui/public/gate.js +2 -2
- package/modules/hall-ui/public/gate.states.json +1 -1
- package/modules/hall-ui/public/hall-render.js +43 -79
- package/modules/hall-ui/public/index.html +5 -1
- package/modules/hall-ui/public/modules.js +1 -0
- package/modules/hall-ui/public/oversight.css +5 -5
- package/modules/hall-ui/public/palette.js +0 -11
- package/modules/hall-ui/public/primer.js +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings-sessions.js +1 -1
- package/modules/hall-ui/public/settings.css +0 -1
- package/modules/hall-ui/public/settings.html +4 -33
- package/modules/hall-ui/public/settings.js +3 -33
- package/modules/hall-ui/public/settings.states.json +2 -2
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/style.css +10 -13
- package/modules/hall-ui/public/task.html +4 -1
- package/modules/hall-ui/public/task.js +36 -1
- package/modules/hall-ui/public/tweak-editor-lib.js +18 -1
- package/modules/hall-ui/public/tweak-editor.css +16 -0
- package/modules/hall-ui/public/tweak-editor.html +26 -3
- package/modules/hall-ui/public/tweak-editor.js +32 -1
- package/modules/hall-ui/public/work.js +1 -1
- package/modules/hall-ui/records/tweak-editor.md +1 -0
- package/modules/ideas/module.json +1 -0
- package/modules/ideas/projection.js +1 -1
- package/modules/lifecycle/db-claim-reads.js +1 -58
- package/modules/lifecycle/db-ship.js +4 -9
- package/modules/lifecycle/db.js +0 -4
- package/modules/lifecycle/github-push.js +12 -14
- package/modules/lifecycle/kickoff-checklist.js +0 -4
- package/modules/lifecycle/lifecycle.js +0 -8
- package/modules/lifecycle/module.json +1 -0
- package/modules/lifecycle/publish-reconciler.js +10 -10
- package/modules/lifecycle/routes/tasks.js +13 -15
- package/modules/lifecycle/ship-card.js +1 -1
- package/modules/lifecycle/task-visuals.js +2 -2
- package/modules/memory/module.json +1 -0
- package/modules/memory/routes/memory.js +2 -2
- package/modules/npm-release/module.json +1 -0
- package/modules/onboarding/module.json +1 -0
- package/modules/onboarding/onboarding-state.js +17 -36
- package/modules/onboarding/routes/access-requests.js +3 -3
- package/modules/platform-identity/module.json +1 -0
- package/modules/platform-identity/platform-identity.js +13 -5
- package/modules/platform-identity/routes/sso.js +5 -4
- package/modules/platform-identity/tests/platform-identity.mjs +17 -3
- package/modules/provisioning/capacity.js +1 -1
- package/modules/provisioning/module.json +1 -0
- package/modules/provisioning/provisioning.js +6 -5
- package/modules/provisioning/routes/provisioning.js +4 -4
- package/modules/provisioning/starter-bundles.js +5 -25
- package/modules/public-landing/module.json +1 -0
- package/modules/public-landing/public/assets/cosmos.css +1 -1
- package/modules/public-landing/public/contact.html +1 -1
- package/modules/public-landing/public/privacy.html +1 -1
- package/modules/public-landing/public/projects.html +101 -32
- package/modules/public-landing/public/projects.probes.json +2 -2
- package/modules/public-landing/public/projects.states.json +4 -3
- package/modules/public-landing/public/terms.html +1 -1
- package/modules/security/module.json +1 -0
- package/modules/sessions/db.js +1 -1
- package/modules/sessions/module.json +1 -0
- package/modules/specialities/module.json +1 -0
- package/modules/status-ui/module.json +1 -0
- package/modules/ui-design/module.json +1 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +84 -0
- package/scripts/gds/artifact-format.js +203 -0
- package/scripts/gds/artifact-staleness.js +2 -2
- package/scripts/gds/audit-authorship.sh +1 -13
- package/scripts/gds/backfill-task-rewards.js +5 -5
- package/scripts/gds/claim.js +0 -15
- package/scripts/gds/claude-materialize.js +2 -2
- package/scripts/gds/cli-lib.js +15 -15
- package/scripts/gds/codemod-rename-src-gds.js +1 -1
- package/scripts/gds/context-pack.js +5 -7
- package/scripts/gds/diagram-facts.js +12 -23
- package/scripts/gds/do-api.js +13 -80
- package/scripts/gds/doc-cli-guard.js +2 -0
- package/scripts/gds/doctor.js +4 -4
- package/scripts/gds/feedback-latest.js +139 -9
- package/scripts/gds/fitness-checks-identity.js +2 -7
- package/scripts/gds/fitness-checks-packaging.js +4 -4
- package/scripts/gds/fitness-checks-write-validation.js +4 -0
- package/scripts/gds/fitness-ratchets.js +5 -5
- package/scripts/gds/fitness.js +10 -10
- package/scripts/gds/gen-api-client.js +4 -4
- package/scripts/gds/gen-api-docs.js +1 -1
- package/scripts/gds/gen-diagrams.js +49 -71
- package/scripts/gds/gen-repo-map.js +13 -14
- package/scripts/gds/gen-session-index.js +8 -8
- package/scripts/gds/http-api-client.js +12 -19
- package/scripts/gds/init.js +1 -1
- package/scripts/gds/leak-scan-allowlist.js +1 -1
- package/scripts/gds/lint-infra-exec.js +1 -1
- package/scripts/gds/local-preview-lib.js +8 -39
- package/scripts/gds/local-preview.js +2 -15
- package/scripts/gds/migration-namespace.js +3 -3
- package/scripts/gds/module-artifact.js +212 -0
- package/scripts/gds/module.js +95 -6
- package/scripts/gds/package-core.js +5 -116
- package/scripts/gds/plain-cards.js +1 -1
- package/scripts/gds/provision-config.js +2 -2
- package/scripts/gds/provision-net.js +2 -3
- package/scripts/gds/provision.js +9 -9
- package/scripts/gds/publish-manifest.js +1 -2
- package/scripts/gds/push-path-brief.js +10 -10
- package/scripts/gds/regen-instance-docs.js +1 -1
- package/scripts/gds/rename-history-check.js +1 -1
- package/scripts/gds/run-unit-tests.js +2 -16
- package/scripts/gds/sandbox-stage.js +39 -341
- package/scripts/gds/session-digest-build.js +2 -2
- package/scripts/gds/ship-deploy-target.js +13 -13
- package/scripts/gds/ship-flow.js +7 -7
- package/scripts/gds/ship-land.js +9 -10
- package/scripts/gds/ship-merge.js +4 -4
- package/scripts/gds/ship-preflight-steps.js +4 -4
- package/scripts/gds/ship-regen.js +15 -49
- package/scripts/gds/ship.js +13 -33
- package/scripts/gds/skill-preflight.js +9 -8
- package/scripts/gds/start.js +0 -15
- package/scripts/hall-preview/README.md +5 -5
- package/scripts/hall-preview/server.js +6 -6
- package/scripts/render-diagrams.sh +1 -1
- package/src/bongos/api-errors.js +1 -1
- package/src/bongos/api-prefix.js +4 -4
- package/src/bongos/auth-github.js +2 -3
- package/src/bongos/auth.js +51 -126
- package/src/bongos/db-kernel.js +2 -3
- package/src/bongos/db.js +5 -28
- package/src/bongos/module-scope-map.js +14 -29
- package/src/bongos/module-store.js +141 -0
- package/src/bongos/platform-visibility-gate.js +1 -1
- package/src/bongos/route-rank-check.js +3 -16
- package/src/bongos/routes/auth.js +0 -116
- package/src/bongos/routes/builders.js +5 -15
- package/src/bongos/routes/instance.js +8 -14
- package/src/bongos/routes/me.js +7 -23
- package/src/bongos/routes/modules.js +83 -0
- package/src/bongos/routes/security.js +3 -3
- package/src/bongos/routes.js +7 -2
- package/src/bongos/serve-internal.js +5 -6
- package/src/bongos-downloads.js +1 -6
- package/src/branding.js +2 -2
- package/src/build-info.js +4 -6
- package/src/instance-config.js +2 -2
- package/src/module-api.js +13 -6
- package/src/module-loader/catalog.js +3 -0
- package/src/module-loader/loader.js +4 -4
- package/src/module-loader/manifest-schema.js +16 -8
- package/src/module-seams.js +2 -3
- package/src/modules.js +42 -14
- package/tests/agents_spend_guard.mjs +1 -1
- package/tests/api_alias_caller_ratchet.mjs +1 -1
- package/tests/api_alias_exceptions.mjs +1 -1
- package/tests/api_client.mjs +1 -16
- package/tests/api_path_404.mjs +1 -1
- package/tests/auth_page_gate.mjs +10 -10
- package/tests/backfill_task_rewards.mjs +1 -1
- package/tests/canonical_profile_url.mjs +1 -3
- package/tests/claim_action.mjs +4 -1
- package/tests/claim_from_task_and_home.mjs +127 -0
- package/tests/{box_ship_permissions.mjs → cli_allowlist_permissions.mjs} +5 -5
- package/tests/cli_sessions.mjs +1 -1
- package/tests/consumer_layout_boot.mjs +2 -0
- package/tests/context_pack.mjs +13 -13
- package/tests/copy_desk_page_docx.mjs +33 -2
- package/tests/copy_inventory.mjs +7 -7
- package/tests/core_upgrade_runner.mjs +6 -6
- package/tests/credit_grant.mjs +2 -2
- package/tests/criterion_suggest.mjs +1 -1
- package/tests/deploy_divergence_line.mjs +11 -11
- package/tests/design_tokens_sync.mjs +3 -2
- package/tests/diagram_facts_offset.mjs +1 -1
- package/tests/do_api.mjs +106 -0
- package/tests/feedback_latest.mjs +122 -0
- package/tests/fitness.mjs +44 -42
- package/tests/fitness_ratchets.mjs +2 -2
- package/tests/gate_approvals.mjs +0 -1
- package/tests/github_push_land_proof.mjs +1 -1
- package/tests/go_live.mjs +3 -3
- package/tests/goal_advisory.mjs +3 -3
- package/tests/goal_suggest.mjs +3 -3
- package/tests/government_abuse_matrix.mjs +21 -19
- package/tests/government_ownership_scope.mjs +33 -35
- package/tests/government_parity.mjs +2 -2
- package/tests/government_protected_surfaces.mjs +56 -37
- package/tests/government_require_permission.mjs +3 -3
- package/tests/government_seed.mjs +37 -4
- package/tests/government_session_scope.mjs +92 -366
- package/tests/hall_audit.mjs +1 -1
- package/tests/hall_error_envelope.mjs +4 -0
- package/tests/hall_landing_boot.mjs +4 -1
- package/tests/hall_palette.mjs +1 -16
- package/tests/hall_record_world.mjs +6 -0
- package/tests/hall_tweak_editor.mjs +52 -1
- package/tests/helpers.mjs +1 -1
- package/tests/host_topology_skips.mjs +3 -4
- package/tests/html_comment_nesting.mjs +137 -0
- package/tests/infra_exec_paths.mjs +9 -10
- package/tests/init.mjs +4 -5
- package/tests/instance_manifest.mjs +18 -25
- package/tests/kickoff_checklist.mjs +1 -1
- package/tests/lib_sh_resolution.mjs +0 -221
- package/tests/local_preview.mjs +0 -12
- package/tests/migration_namespace.mjs +6 -6
- package/tests/module-scope-map.mjs +9 -9
- package/tests/module_api.mjs +2 -2
- package/tests/module_catalog.mjs +1 -1
- package/tests/module_cli.mjs +20 -20
- package/tests/module_contributions.mjs +6 -6
- package/tests/module_loader.mjs +6 -6
- package/tests/module_manifest.mjs +26 -17
- package/tests/module_route_rank.mjs +2 -2
- package/tests/module_store_publish.mjs +307 -0
- package/tests/module_store_publish_route.mjs +121 -0
- package/tests/module_store_registry_migration.mjs +70 -0
- package/tests/modules.mjs +56 -26
- package/tests/modules_route_wiring.mjs +1 -1
- package/tests/onboarding_route_signals.mjs +21 -31
- package/tests/onboarding_state.mjs +35 -49
- package/tests/paste_token_session_store.mjs +1 -1
- package/tests/permission_path.mjs +0 -3
- package/tests/platform_boot.mjs +1 -2
- package/tests/platform_visibility_gate.mjs +1 -1
- package/tests/profile_route.mjs +1 -6
- package/tests/project_modules_ui.mjs +17 -17
- package/tests/projects_hub.mjs +5 -5
- package/tests/projects_hub_app_step.mjs +138 -0
- package/tests/projects_hub_module_picker.mjs +77 -76
- package/tests/projects_hub_pre_uat.mjs +4 -5
- package/tests/provision.mjs +7 -7
- package/tests/provisioning_capacity.mjs +1 -1
- package/tests/provisioning_recommendations.mjs +2 -1
- package/tests/provisioning_starter_bundles.mjs +0 -22
- package/tests/publish_branch_route.mjs +11 -19
- package/tests/publish_manifest.mjs +2 -3
- package/tests/publish_reconciler.mjs +8 -8
- package/tests/push_path_brief.mjs +10 -9
- package/tests/rank_tier_single_source.mjs +1 -13
- package/tests/regrade_eligibility.mjs +1 -1
- package/tests/rename_history_restraint.mjs +1 -1
- package/tests/repo_map.mjs +12 -13
- package/tests/runner_drift.mjs +3 -21
- package/tests/sandbox_stage.mjs +57 -390
- package/tests/seam_wiring_guard.mjs +11 -11
- package/tests/search_isolation.mjs +3 -3
- package/tests/session_records.mjs +1 -1
- package/tests/session_rename_fallback.mjs +1 -22
- package/tests/session_start_freshness.mjs +3 -3
- package/tests/session_token_hash_db.mjs +1 -1
- package/tests/ship_card.mjs +1 -1
- package/tests/ship_ci_deploy.mjs +17 -17
- package/tests/ship_error_shape.mjs +1 -1
- package/tests/ship_premerge.mjs +1 -1
- package/tests/ship_resume.mjs +1 -1
- package/tests/skill_preflight.mjs +2 -2
- package/tests/skip_is_not_pass.mjs +1 -1
- package/tests/task_visual_slots.mjs +31 -0
- package/tests/terms_acceptance.mjs +2 -2
- package/tests/upgrade.mjs +4 -4
- package/tests/watch_sealed_floor.mjs +0 -1
- package/tests/wizard_draft_resume.mjs +2 -2
- package/tests/wizard_intent_resume.mjs +21 -10
- package/tests/wizard_preselect_why.mjs +18 -18
- package/docs/onboarding/browser-terminal-guide.md +0 -73
- package/docs/recipes/managed-settings-remote-control.md +0 -416
- package/modules/dev-box/CLAUDE.md +0 -15
- package/modules/dev-box/box-access.js +0 -623
- package/modules/dev-box/box-credential.js +0 -94
- package/modules/dev-box/box-onboard.js +0 -486
- package/modules/dev-box/boxes.js +0 -1038
- package/modules/dev-box/db.js +0 -39
- package/modules/dev-box/module.json +0 -19
- package/modules/dev-box/routes/box.js +0 -1223
- package/scripts/gds/box-auth-check.js +0 -160
- package/scripts/gds/box-infra.js +0 -321
- package/scripts/gds/box-sync.js +0 -216
- package/scripts/gds/box.js +0 -1332
- package/scripts/gds/cf-tunnel.js +0 -558
- package/scripts/gds/smoke-box.sh +0 -150
- package/scripts/gds/tree-preflight.js +0 -241
- package/src/bongos/app-pair.js +0 -172
- package/tests/app_pair.mjs +0 -168
- package/tests/box_access.mjs +0 -578
- package/tests/box_auth_check.mjs +0 -114
- package/tests/box_code_staleness.mjs +0 -129
- package/tests/box_connect_e2e.mjs +0 -196
- package/tests/box_cost.mjs +0 -70
- package/tests/box_credential.mjs +0 -125
- package/tests/box_dns_repoint.mjs +0 -153
- package/tests/box_env.mjs +0 -64
- package/tests/box_host_keys.mjs +0 -126
- package/tests/box_infra_resolve.mjs +0 -246
- package/tests/box_onboard.mjs +0 -317
- package/tests/box_scope_session.mjs +0 -119
- package/tests/box_sweep_docs.mjs +0 -119
- package/tests/box_sync.mjs +0 -55
- package/tests/box_sync_scope_report.mjs +0 -126
- package/tests/box_task_scope.mjs +0 -169
- package/tests/box_task_scope_pause.mjs +0 -66
- package/tests/box_terminal_ssrf.mjs +0 -96
- package/tests/boxes.mjs +0 -1585
- package/tests/cf_ruleset_preflight.mjs +0 -142
- package/tests/cf_tunnel.mjs +0 -496
- package/tests/docs_scrubber_damage.mjs +0 -111
- package/tests/module_scope_active_claims.mjs +0 -94
- package/tests/ship_api_client_pathspec.mjs +0 -100
- package/tests/tree_preflight.mjs +0 -243
|
@@ -46,12 +46,12 @@ const nothingExists = () => false;
|
|
|
46
46
|
// ---------------------------------------------------------------------------
|
|
47
47
|
|
|
48
48
|
test('reports a declared poller whose file was never shipped, naming module + seam', () => {
|
|
49
|
-
const r = scanSeamWiring([mod('
|
|
49
|
+
const r = scanSeamWiring([mod('discord', { pollers: ['ghost'] })], { exists: nothingExists });
|
|
50
50
|
assert.equal(r.ok, false);
|
|
51
51
|
assert.equal(r.findings.length, 1);
|
|
52
|
-
assert.match(r.findings[0], /module "
|
|
52
|
+
assert.match(r.findings[0], /module "discord"/);
|
|
53
53
|
assert.match(r.findings[0], /contributes\.pollers: "ghost"/);
|
|
54
|
-
assert.match(r.findings[0], /modules\/
|
|
54
|
+
assert.match(r.findings[0], /modules\/discord\/pollers\/ghost\.js does not exist/);
|
|
55
55
|
// and it says what to do about it — the three legitimate resolutions
|
|
56
56
|
assert.match(r.findings[0], /Ship the artifact, drop the declaration, or record it in declarativeSeams\.pollers/);
|
|
57
57
|
});
|
|
@@ -100,39 +100,39 @@ test('a disciplines entry absent from the registry is reported', () => {
|
|
|
100
100
|
|
|
101
101
|
test('a declarative-only entry is recorded, not reported — the load-bearing nuance', () => {
|
|
102
102
|
const r = scanSeamWiring(
|
|
103
|
-
[mod('
|
|
103
|
+
[mod('discord', { pollers: ['discord-bot'] }, { pollers: ['discord-bot'] })],
|
|
104
104
|
{ exists: nothingExists },
|
|
105
105
|
);
|
|
106
106
|
assert.equal(r.ok, true, r.findings.join('; '));
|
|
107
|
-
assert.deepEqual(r.declarative, [{ key: '
|
|
107
|
+
assert.deepEqual(r.declarative, [{ key: 'discord', seam: 'pollers', entry: 'discord-bot' }]);
|
|
108
108
|
assert.deepEqual(r.wired, []);
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
test('the exemption is per-ENTRY, not a blanket skip of the seam', () => {
|
|
112
112
|
const r = scanSeamWiring(
|
|
113
|
-
[mod('
|
|
113
|
+
[mod('discord', { pollers: ['discord-bot', 'ghost'] }, { pollers: ['discord-bot'] })],
|
|
114
114
|
{ exists: nothingExists },
|
|
115
115
|
);
|
|
116
116
|
assert.equal(r.ok, false);
|
|
117
117
|
assert.equal(r.findings.length, 1);
|
|
118
118
|
assert.match(r.findings[0], /"ghost"/);
|
|
119
|
-
assert.deepEqual(r.declarative.map((d) => d.entry), ['
|
|
119
|
+
assert.deepEqual(r.declarative.map((d) => d.entry), ['discord-bot']);
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
test('a STALE exemption — the artifact exists now — is itself reported', () => {
|
|
123
123
|
const r = scanSeamWiring(
|
|
124
|
-
[mod('
|
|
125
|
-
{ exists: only('modules/
|
|
124
|
+
[mod('discord', { pollers: ['discord-bot'] }, { pollers: ['discord-bot'] })],
|
|
125
|
+
{ exists: only('modules/discord/pollers/discord-bot.js') },
|
|
126
126
|
);
|
|
127
127
|
assert.equal(r.ok, false);
|
|
128
|
-
assert.match(r.findings[0], /exempts pollers "
|
|
128
|
+
assert.match(r.findings[0], /exempts pollers "discord-bot" as declarative-only, but modules\/discord\/pollers\/discord-bot\.js exists/);
|
|
129
129
|
assert.match(r.findings[0], /drop the stale declarativeSeams\.pollers entry/);
|
|
130
130
|
assert.deepEqual(r.declarative, [], 'a stale exemption must not also count as declarative-only');
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
test('an exemption on one seam does not leak to another seam of the same module', () => {
|
|
134
134
|
const r = scanSeamWiring(
|
|
135
|
-
[mod('
|
|
135
|
+
[mod('discord', { pollers: ['x'], uiSections: ['x'] }, { pollers: ['x'] })],
|
|
136
136
|
{ exists: nothingExists },
|
|
137
137
|
);
|
|
138
138
|
assert.equal(r.findings.length, 1);
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
// DB in a rolled-back transaction at ship time (the authoritative evidence).
|
|
22
22
|
//
|
|
23
23
|
// Run: node tests/search_isolation.mjs
|
|
24
|
-
// (Layer C runs only where a Postgres is reachable — the integration gate /
|
|
25
|
-
//
|
|
24
|
+
// (Layer C runs only where a Postgres is reachable — the integration gate / any
|
|
25
|
+
// machine with one; it skips cleanly otherwise so the DB-free unit gate stays green.)
|
|
26
26
|
|
|
27
27
|
import { strict as assert } from 'node:assert';
|
|
28
28
|
import { createRequire } from 'node:module';
|
|
@@ -159,7 +159,7 @@ async function dbReachable() {
|
|
|
159
159
|
|
|
160
160
|
const client = await dbReachable();
|
|
161
161
|
if (!client) {
|
|
162
|
-
console.log(' SKIP no reachable Postgres — Layer C runs in the integration gate /
|
|
162
|
+
console.log(' SKIP no reachable Postgres — Layer C runs in the integration gate / wherever a Postgres is reachable.');
|
|
163
163
|
console.log(' (The same scoping cases were proven on the prod DB in a rolled-back transaction at ship time.)');
|
|
164
164
|
// END THE POOL ON THE SKIP PATH TOO (task 1003848). Only the else-branch below
|
|
165
165
|
// ended it, so when Postgres was unreachable the Pool survived with libuv handles
|
|
@@ -397,7 +397,7 @@ test('buildSessionDigest accumulates per-model usage (model_usage), skipping syn
|
|
|
397
397
|
});
|
|
398
398
|
|
|
399
399
|
test('buildSessionDigest (task 1676): folds unlabeled token-bearing turns into a priceable model bucket', () => {
|
|
400
|
-
// The
|
|
400
|
+
// The CI symptom: a token-bearing turn with no model id (or '<synthetic>')
|
|
401
401
|
// contributes to total_tokens but was DROPPED from model_usage — so the digest
|
|
402
402
|
// stored total_tokens>0 yet priced to $0 (ADR 0097). The fix attributes the
|
|
403
403
|
// unaccounted remainder to a real, priceable family bucket so cost-plus pays.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Two artefacts get renamed here, and the whole risk is in the fallback, not the rename:
|
|
4
4
|
//
|
|
5
5
|
// 1. ~/.config/<instance>/gds-session.json → bongos-session.json. This file is on disk on every
|
|
6
|
-
// builder machine
|
|
6
|
+
// builder machine. Renaming it with no fallback signs all of them out
|
|
7
7
|
// at once and every /builder-* command dies with 'no Bongos session'.
|
|
8
8
|
// 2. The `gds_session` browser cookie → `bongos_session`. Renaming it logs out every signed-in
|
|
9
9
|
// session, the owner's included. The read-list is THREE names, because `pms_session` predates
|
|
@@ -263,23 +263,6 @@ test('the browser logout clears every session-cookie name', () => {
|
|
|
263
263
|
}
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
-
// ── 6. the box scripts read both names ──────────────────────────────────────────────────────
|
|
267
|
-
|
|
268
|
-
test('the baked box scripts scan BOTH session filenames', () => {
|
|
269
|
-
// These run on live dev boxes and are baked at provision time, so they cannot import anything.
|
|
270
|
-
// A box provisioned AFTER the rename writes only the new name; one provisioned before has only
|
|
271
|
-
// the old. Missing either silently breaks heartbeat/reporting on half the fleet.
|
|
272
|
-
for (const rel of ['infra/box-heartbeat.sh', 'infra/box-report-host-keys.sh',
|
|
273
|
-
'infra/box-report-terminal.sh', 'infra/box-report-version.sh']) {
|
|
274
|
-
const src = read(rel);
|
|
275
|
-
const glob = src.split('\n').find((l) => l.includes('for _sess in'));
|
|
276
|
-
assert.ok(glob, `${rel}: session glob not found`);
|
|
277
|
-
for (const name of [NEW_FILE, 'gds-session.json']) {
|
|
278
|
-
assert.ok(glob.includes(name), `${rel}: glob must scan ${name} — got: ${glob.trim()}`);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
|
|
283
266
|
// ── 8. the credential detector follows the rename ───────────────────────────────────────────
|
|
284
267
|
|
|
285
268
|
test('the artifact-scan credential detector flags a read of EVERY session filename', () => {
|
|
@@ -295,7 +278,3 @@ test('the artifact-scan credential detector flags a read of EVERY session filena
|
|
|
295
278
|
assert.ok(rule.includes(stem), `the detector must still match ${name} — got: ${rule.trim()}`);
|
|
296
279
|
}
|
|
297
280
|
});
|
|
298
|
-
// The box resolver is EXECUTED — a post-rename box and a migrated one — in
|
|
299
|
-
// tests/lib_sh_resolution.mjs, which already owns the extraction harness for that shell
|
|
300
|
-
// block. Deliberately not duplicated here: two harnesses on one block is two things to
|
|
301
|
-
// fix when the names next move (task 1003704).
|
|
@@ -66,7 +66,7 @@ test('clean main full clone, strictly behind → pull', () => {
|
|
|
66
66
|
|
|
67
67
|
// ---------- pull: a clean, strictly-behind checkout self-heals (task 1284) ----------
|
|
68
68
|
// FF is now safe on ANY branch and ANY clone type when ahead===0 and the tree is
|
|
69
|
-
// clean — that's what lets a
|
|
69
|
+
// clean — that's what lets a worktree (feature branch off a partial clone)
|
|
70
70
|
// stop silently running stale code.
|
|
71
71
|
|
|
72
72
|
test('feature branch, clean, ahead 0 → pull (self-heal, not warn)', () => {
|
|
@@ -75,13 +75,13 @@ test('feature branch, clean, ahead 0 → pull (self-heal, not warn)', () => {
|
|
|
75
75
|
assert.equal(d.reason, 'safe-fast-forward');
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
test('partial/blobless clone, clean, ahead 0 → pull (
|
|
78
|
+
test('partial/blobless clone, clean, ahead 0 → pull (partial clones self-heal)', () => {
|
|
79
79
|
const d = decideFreshness({ ...SAFE, isPartialClone: true });
|
|
80
80
|
assert.equal(d.action, 'pull');
|
|
81
81
|
assert.equal(d.reason, 'safe-fast-forward');
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
test('the
|
|
84
|
+
test('the partial-clone worktree case: feature branch + partial clone + clean + ahead 0 → pull', () => {
|
|
85
85
|
const d = decideFreshness({ behind: 12, ahead: 0, branch: 'claude/poincare', hasTrackedChanges: false, isPartialClone: true });
|
|
86
86
|
assert.equal(d.action, 'pull');
|
|
87
87
|
assert.equal(d.reason, 'safe-fast-forward');
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// (2) is the one worth a real database. It is the whole reason the migration
|
|
13
13
|
// backfills instead of letting sessions re-mint, and it can only be proved by
|
|
14
14
|
// running the actual SQL against rows in the actual old shape. A mistake here
|
|
15
|
-
// does not fail loudly in review — it signs out every builder
|
|
15
|
+
// does not fail loudly in review — it signs out every builder
|
|
16
16
|
// on deploy day, including whoever would have to log in to fix it.
|
|
17
17
|
//
|
|
18
18
|
// The sibling `session_token_hash.mjs` is DB-free and pins the hash itself plus
|
package/tests/ship_card.mjs
CHANGED
|
@@ -127,7 +127,7 @@ test('confirmed + bailed land: says NOT landed, names the reason, warns', () =>
|
|
|
127
127
|
});
|
|
128
128
|
|
|
129
129
|
test('confirmed + bailed land: the bail reaches the printed monospace card', () => {
|
|
130
|
-
// The monospace card is the ALWAYS-printed fallback (headless, cron
|
|
130
|
+
// The monospace card is the ALWAYS-printed fallback (headless, cron).
|
|
131
131
|
// If the reason stops at the model, the builder still never sees it.
|
|
132
132
|
const out = card.renderMonospaceCard(card.buildCardModel(bailed));
|
|
133
133
|
assert.match(out, /NOT landed/);
|
package/tests/ship_ci_deploy.mjs
CHANGED
|
@@ -298,8 +298,8 @@ test('pending is NOT a bail: no reason, so the card keeps "landing automatically
|
|
|
298
298
|
assert.equal(o.reason, null);
|
|
299
299
|
});
|
|
300
300
|
|
|
301
|
-
test('strandCommand: a
|
|
302
|
-
// /merge-mode needs gh + a main checkout a
|
|
301
|
+
test('strandCommand: a server-publish checkout gets the resume, a laptop gets /merge-mode', () => {
|
|
302
|
+
// /merge-mode needs gh + a main checkout a credential-less checkout doesn't have (task 1052).
|
|
303
303
|
assert.equal(strandCommand(77, 'server'), 'node scripts/gds/ship.js 77');
|
|
304
304
|
assert.equal(strandCommand(77, 'local'), '/merge-mode');
|
|
305
305
|
});
|
|
@@ -352,11 +352,11 @@ test('config/deploy.json — when present ships as ci; absent is fine on the neu
|
|
|
352
352
|
'To roll back to operator-laptop deploys, set mode "laptop" AND disarm the PROD_DEPLOY_VIA_CI repo var.');
|
|
353
353
|
});
|
|
354
354
|
|
|
355
|
-
// ---- 6. shouldBailOnFetchFailure: the task-1037
|
|
355
|
+
// ---- 6. shouldBailOnFetchFailure: the task-1037 server-publish fix ----
|
|
356
356
|
//
|
|
357
357
|
// autoMerge() ran `git fetch origin main` as a HARD precondition before
|
|
358
|
-
// dispatching to the land path. A credential-less
|
|
359
|
-
//
|
|
358
|
+
// dispatching to the land path. A credential-less checkout (private origin,
|
|
359
|
+
// no clone token) can't fetch, so it bailed here and stranded
|
|
360
360
|
// every ship at confirmed — never reaching the ADR-0055 server-mediated publish
|
|
361
361
|
// built precisely for it (the bug task 1027 surfaced). The fix makes a fetch
|
|
362
362
|
// failure fatal ONLY off the server path. Truth table, proven without git:
|
|
@@ -367,8 +367,8 @@ test('shouldBailOnFetchFailure: fetch OK never bails (regardless of path)', () =
|
|
|
367
367
|
assert.equal(shouldBailOnFetchFailure(true, 'laptop', 'local'), false);
|
|
368
368
|
});
|
|
369
369
|
|
|
370
|
-
test('shouldBailOnFetchFailure: fetch FAILED on the
|
|
371
|
-
// The fix: ci + server (a credential-less
|
|
370
|
+
test('shouldBailOnFetchFailure: fetch FAILED on the server-publish path does NOT bail', () => {
|
|
371
|
+
// The fix: ci + server (a credential-less checkout) proceeds to ciLandServer, which
|
|
372
372
|
// bundles against the local origin/main while the server tops up main.
|
|
373
373
|
assert.equal(shouldBailOnFetchFailure(false, 'ci', 'server'), false);
|
|
374
374
|
});
|
|
@@ -424,15 +424,15 @@ test('classifyLandOutcome: NOT merged + deploy unconfigured → pending, never s
|
|
|
424
424
|
assert.equal(classifyLandOutcome({ pr_state: 'closed', merged: false, deploy_status: 'unconfigured' }), 'failed');
|
|
425
425
|
});
|
|
426
426
|
|
|
427
|
-
// ---- 8. strandNextStep:
|
|
428
|
-
// The #1027 retrospective: a confirmed-strand on a
|
|
429
|
-
// /merge-mode, which needs gh + a main checkout
|
|
427
|
+
// ---- 8. strandNextStep: recovery guidance that fits the push path, never gh-only /merge-mode on the server path (task 1052) ----
|
|
428
|
+
// The #1027 retrospective: a confirmed-strand on a server-publish checkout pointed the builder at
|
|
429
|
+
// /merge-mode, which needs gh + a main checkout that checkout lacks — a dead end.
|
|
430
430
|
|
|
431
|
-
test('strandNextStep: a
|
|
431
|
+
test('strandNextStep: a server-publish checkout (via "server") is pointed at resume, NOT /merge-mode', () => {
|
|
432
432
|
const s = strandNextStep(1027, 'server');
|
|
433
433
|
assert.match(s, /ship\.js 1027/, 'names the resume command with the task id');
|
|
434
434
|
assert.match(s, /resume|resumes/i);
|
|
435
|
-
assert.match(s, /NOT available/, 'explicitly says /merge-mode is not available
|
|
435
|
+
assert.match(s, /NOT available/, 'explicitly says /merge-mode is not available there');
|
|
436
436
|
});
|
|
437
437
|
|
|
438
438
|
test('strandNextStep: a laptop (via "local") keeps /merge-mode, with resume as the alternative', () => {
|
|
@@ -441,25 +441,25 @@ test('strandNextStep: a laptop (via "local") keeps /merge-mode, with resume as t
|
|
|
441
441
|
assert.match(s, /ship\.js 1027/, 'and resume is offered as the alternative');
|
|
442
442
|
});
|
|
443
443
|
|
|
444
|
-
test('task 1046: regenerateDiagrams SKIPS on the
|
|
444
|
+
test('task 1046: regenerateDiagrams SKIPS on the server-publish path and leaves the tree untouched', () => {
|
|
445
445
|
const { execSync } = require('node:child_process');
|
|
446
446
|
const ROOT = path.resolve(__dirname, '..');
|
|
447
447
|
const status = () => execSync('git status --porcelain docs/onboarding/diagrams', { cwd: ROOT }).toString();
|
|
448
448
|
const before = status();
|
|
449
449
|
const prev = process.env.OTB_PUSH_VIA_SERVER;
|
|
450
|
-
process.env.OTB_PUSH_VIA_SERVER = '1'; // force pushVia()==='server' (a credential-less
|
|
450
|
+
process.env.OTB_PUSH_VIA_SERVER = '1'; // force pushVia()==='server' (a credential-less checkout)
|
|
451
451
|
try {
|
|
452
|
-
// On
|
|
452
|
+
// On the server-publish path there is no headless browser; attempting the render strands the
|
|
453
453
|
// regenerated .mmd + assertions.json UNCOMMITTED in the tree (#1046). The
|
|
454
454
|
// guard must return early BEFORE any regen/render, so the call is a pure no-op.
|
|
455
455
|
assert.equal(ship.regenerateDiagrams(), 'skipped-server',
|
|
456
|
-
'on
|
|
456
|
+
'on the server-publish path the diagram regen must skip entirely (no render attempt)');
|
|
457
457
|
} finally {
|
|
458
458
|
if (prev === undefined) delete process.env.OTB_PUSH_VIA_SERVER;
|
|
459
459
|
else process.env.OTB_PUSH_VIA_SERVER = prev;
|
|
460
460
|
}
|
|
461
461
|
assert.equal(status(), before,
|
|
462
|
-
'#1046: the
|
|
462
|
+
'#1046: the server-publish skip must not write or stage any diagram artifact (no uncommitted .mmd/assertions left behind)');
|
|
463
463
|
});
|
|
464
464
|
|
|
465
465
|
// ---- 9. pickStaleGateReviewRun: the gate-author-trust re-run nudge (task 1169) ----
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// * interpolating `data.error` into a template string stringifies the
|
|
12
12
|
// envelope OBJECT — that is the literal "[object Object]";
|
|
13
13
|
// * `data.error === 'some_code'` never matches, so the graceful branch
|
|
14
|
-
// behind it (
|
|
14
|
+
// behind it (the re-auth hint, verify-attestation remedy, the
|
|
15
15
|
// CLAIM_SESSION_MISMATCH explanation) is DEAD CODE against a live server;
|
|
16
16
|
// * `data.<field>` reads a top-level field that MOVED into `error.details`.
|
|
17
17
|
//
|
package/tests/ship_premerge.mjs
CHANGED
|
@@ -43,7 +43,7 @@ t('decidePreMerge: single commit behind still merges', () => {
|
|
|
43
43
|
assert.equal(ship.decidePreMerge({ branch: 'claude/feat', behind: 1 }).action, 'merge');
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
// idea #364: a credential-less partial-clone
|
|
46
|
+
// idea #364: a credential-less partial-clone checkout (pushVia 'server') cannot run
|
|
47
47
|
// the local `git merge origin/main` (no creds to lazily fetch main's blobs) and
|
|
48
48
|
// does not need to — the server reconciles with main at publish time (ADR 0055).
|
|
49
49
|
// So the server path SKIPS the pre-merge regardless of how far behind it is, while
|
package/tests/ship_resume.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// strands the task with no claim:
|
|
9
9
|
// - grade fail → task stuck at `completed` → re-run should RE-GRADE
|
|
10
10
|
// - merge/deploy fail → task stuck at `confirmed` → re-run should RE-MERGE
|
|
11
|
-
// (the exact task-1027
|
|
11
|
+
// (the exact task-1027 nightmare)
|
|
12
12
|
// evaluateResumeAction is the pure decision behind that routing. We prove the
|
|
13
13
|
// full truth table here — no git, no network.
|
|
14
14
|
//
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// guards-skill-declares-machine.
|
|
3
3
|
//
|
|
4
4
|
// THE INCIDENT. /merge-mode states in its own prose that it assumes the Archon's
|
|
5
|
-
// Mac or the droplet. It was run on a
|
|
6
|
-
// because strandNextStep() tells a
|
|
5
|
+
// Mac or the droplet. It was run on a checkout with no gh and no main checkout anyway — and ship.js
|
|
6
|
+
// ALREADY knew, because strandNextStep() tells such a checkout "/merge-mode is NOT available here". The
|
|
7
7
|
// knowledge existed in two places and neither stopped the run: prose cannot
|
|
8
8
|
// refuse, and the one executable copy sat on a different code path.
|
|
9
9
|
//
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// modules/security/tests/sanitize_rebuild.mjs gated its two physical-confinement symlink
|
|
6
6
|
// cases behind canSymlink() and, in the else-branch, printed `ok` and did `passed += 2`.
|
|
7
|
-
// So on any machine that cannot create symlinks —
|
|
7
|
+
// So on any machine that cannot create symlinks — a Windows machine, a container without
|
|
8
8
|
// CAP_SYMLINK, a non-admin account — the suite reported two passes for two cases that never
|
|
9
9
|
// ran, and those two are the ONLY coverage of physical confinement.
|
|
10
10
|
//
|
|
@@ -157,5 +157,36 @@ await test('attach uploads every slot file in the directory to its NAMED slot, a
|
|
|
157
157
|
assert.equal(code, 0, 'all eight attached');
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
+
// task 1004358: every test above drives the bare module router, which is why a
|
|
161
|
+
// real render 413'd live — buildGdsRouter's global 64 KB express.json runs FIRST
|
|
162
|
+
// and rejected any slot body over 64 KB before the route's own 8 MB parser saw
|
|
163
|
+
// it. So this case puts the REAL gate from buildGdsRouter (its first layer)
|
|
164
|
+
// in front of the same router, and sends a body the size of a real render.
|
|
165
|
+
await test('a slot render over 64 KB gets past buildGdsRouter\'s global body cap and attaches (task 1004358)', async () => {
|
|
166
|
+
const { buildGdsRouter } = require('../src/bongos/routes.js');
|
|
167
|
+
const gate = buildGdsRouter().stack[0].handle;
|
|
168
|
+
const whole = express();
|
|
169
|
+
whole.use(gate);
|
|
170
|
+
whole.use((req, res, next) => { res.fail = (code, status, extra) => res.status(typeof status === 'number' ? status : (status && status.status) || 500).json({ error: code, ...(extra || {}) }); next(); });
|
|
171
|
+
whole.use(require('../modules/lifecycle/routes/visuals.js')());
|
|
172
|
+
whole.use((err, req, res, next) => res.status(err.status || 500).json({ error: err.type || 'error' }));
|
|
173
|
+
const srv = http.createServer(whole);
|
|
174
|
+
await new Promise((r) => srv.listen(0, '127.0.0.1', r));
|
|
175
|
+
const at = `http://127.0.0.1:${srv.address().port}`;
|
|
176
|
+
// A real PNG padded past the cap: ~120 KB, the size of the renders that failed live.
|
|
177
|
+
const big = Buffer.concat([PNG, Buffer.alloc(120 * 1024)]);
|
|
178
|
+
const body = JSON.stringify({ image_b64: big.toString('base64'), content_type: 'image/png' });
|
|
179
|
+
assert.ok(body.length > 64 * 1024, 'the body is over the global cap');
|
|
180
|
+
try {
|
|
181
|
+
const post = (url) => fetch(at + url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body });
|
|
182
|
+
const control = await post('/tasks/5002/not-a-bypassed-path');
|
|
183
|
+
assert.equal(control.status, 413, 'the gate under test really is the 64 KB cap');
|
|
184
|
+
const r = await post('/tasks/5002/visuals/before-desktop-light');
|
|
185
|
+
const j = await r.json();
|
|
186
|
+
assert.equal(r.status, 201, `the render attaches, not 413: ${JSON.stringify(j)}`);
|
|
187
|
+
assert.equal(j.slot, 'before-desktop-light');
|
|
188
|
+
} finally { await new Promise((r) => srv.close(r)); }
|
|
189
|
+
});
|
|
190
|
+
|
|
160
191
|
fs.rmSync(TMP, { recursive: true, force: true });
|
|
161
192
|
summary();
|
|
@@ -495,12 +495,12 @@ await ta('no hub session, no acceptance — the unauthenticated caller is bounce
|
|
|
495
495
|
assert.equal(pool.accounts.size, 0, 'an account (and an acceptance) was created for a caller with no session');
|
|
496
496
|
});
|
|
497
497
|
|
|
498
|
-
await ta('a dev-box
|
|
498
|
+
await ta('a session of an undeclared source (a leftover dev-box token) cannot mint an acceptance either', async () => {
|
|
499
499
|
SESSION = { ...HUB_SESSION, source: 'box' };
|
|
500
500
|
pool.accounts.clear();
|
|
501
501
|
const res = await authorize();
|
|
502
502
|
assert.equal(res.status, 403);
|
|
503
|
-
assert.equal(pool.accounts.size, 0, 'a
|
|
503
|
+
assert.equal(pool.accounts.size, 0, 'a session of an undeclared source recorded a platform T&C acceptance');
|
|
504
504
|
});
|
|
505
505
|
|
|
506
506
|
// ===========================================================================
|
package/tests/upgrade.mjs
CHANGED
|
@@ -1055,7 +1055,7 @@ function moduleLibStub(verdict) {
|
|
|
1055
1055
|
t('preflightModules: a DISABLED incompatible module is returned as a warning, not dropped', () => {
|
|
1056
1056
|
const dir = scratchConsumer();
|
|
1057
1057
|
mkdirSync(join(dir, 'modules'), { recursive: true });
|
|
1058
|
-
const warned = { key: 'stale-catalog-entry', coreVersion: '^1.0.0', enabled: false, satisfied: false, maintainer: 'Ada <ada@example.com>', maintenance: { status: 'maintained' } };
|
|
1058
|
+
const warned = { key: 'stale-catalog-entry', version: '1.0.0', coreVersion: '^1.0.0', enabled: false, satisfied: false, maintainer: 'Ada <ada@example.com>', maintenance: { status: 'maintained' } };
|
|
1059
1059
|
const res = u.preflightModules({ instanceDir: dir, targetVersion: '2.0.0' }, {
|
|
1060
1060
|
moduleLib: moduleLibStub({ ok: true, blocking: [], warnings: [warned], deprecations: [], expired: [], core: '2.0.0' }),
|
|
1061
1061
|
});
|
|
@@ -1084,7 +1084,7 @@ t('preflightModules: no modules/ dir returns every collection empty (a vanilla c
|
|
|
1084
1084
|
t('reportModulePreflight: prints the rotted-but-disabled module WITH its maintainer named', () => {
|
|
1085
1085
|
const out = [];
|
|
1086
1086
|
const mods = {
|
|
1087
|
-
warnings: [{ key: 'stale-catalog-entry', coreVersion: '^1.0.0', maintainer: 'Ada <ada@example.com>', maintenance: { status: 'maintained' } }],
|
|
1087
|
+
warnings: [{ key: 'stale-catalog-entry', version: '1.0.0', coreVersion: '^1.0.0', maintainer: 'Ada <ada@example.com>', maintenance: { status: 'maintained' } }],
|
|
1088
1088
|
deprecations: [], expired: [],
|
|
1089
1089
|
};
|
|
1090
1090
|
u.reportModulePreflight(mods, '2.0.0', { log: (s) => out.push(s), err: (s) => out.push(s) });
|
|
@@ -1122,8 +1122,8 @@ t('runUpgrade: a REFUSED bump still reports the non-blocking module findings', a
|
|
|
1122
1122
|
err: (s) => out.push(String(s)),
|
|
1123
1123
|
moduleLib: moduleLibStub({
|
|
1124
1124
|
ok: false,
|
|
1125
|
-
blocking: [{ key: 'enabled-breaker', coreVersion: '^1.0.0', enabled: true, satisfied: false }],
|
|
1126
|
-
warnings: [{ key: 'stale-catalog-entry', coreVersion: '^1.0.0', maintenance: { status: 'orphaned', note: 'author unreachable' } }],
|
|
1125
|
+
blocking: [{ key: 'enabled-breaker', version: '1.0.0', coreVersion: '^1.0.0', enabled: true, satisfied: false }],
|
|
1126
|
+
warnings: [{ key: 'stale-catalog-entry', version: '1.0.0', coreVersion: '^1.0.0', maintenance: { status: 'orphaned', note: 'author unreachable' } }],
|
|
1127
1127
|
deprecations: [], expired: [], core: '2.0.0',
|
|
1128
1128
|
}),
|
|
1129
1129
|
});
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
// builder.roster.read → metic (ADR 0157, was archon)
|
|
20
20
|
// session.search → metic (ADR 0157, was archon)
|
|
21
21
|
// audit_log.read → metic (ADR 0157, was archon)
|
|
22
|
-
// box.fleet.manage → metic (ADR 0157, was archon)
|
|
23
22
|
//
|
|
24
23
|
// A hardcoded 'metic' is the same defect one ADR later, which is why the fix reads
|
|
25
24
|
// the floor off the DENIAL: requirePermission already rides `details.floors` on
|
|
@@ -90,9 +90,9 @@ test('post-auth: the real step is rendered', () => {
|
|
|
90
90
|
|
|
91
91
|
test('a draft already at step 1 is unchanged, and an out-of-range step is not resurrected', () => {
|
|
92
92
|
assert.equal(runStep({ authKnown: false, draftStep: 1 }).persisted.step, 1);
|
|
93
|
-
//
|
|
93
|
+
// 9 is the done panel (8 before task 1004351), reached by its own branch above this slice; 0 is the gate. Neither
|
|
94
94
|
// is a resumable wizard position, so the placeholder is allowed to stand.
|
|
95
|
-
for (const bad of [0,
|
|
95
|
+
for (const bad of [0, 9, 99]) {
|
|
96
96
|
assert.equal(runStep({ authKnown: false, draftStep: bad }).persisted.step, 1, `step ${bad} is not resumed`);
|
|
97
97
|
}
|
|
98
98
|
});
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
// ruled out the task's other option (drop the record at a terminal state).
|
|
16
16
|
//
|
|
17
17
|
// The reset fires only when there is a SECOND DRAFT to lose, and the draft's own step is
|
|
18
|
-
// what says so. A landed create clears DRAFT_KEY — but show(
|
|
18
|
+
// what says so. A landed create clears DRAFT_KEY — but show(9) then persists the done
|
|
19
19
|
// panel's position straight back into it, so a plain "is there a draft?" test is always
|
|
20
20
|
// yes and would skip the reset forever, leaving the finished project's name sitting in
|
|
21
21
|
// the fresh form. (That is exactly what the first cut of this change shipped into a
|
|
22
22
|
// render: "still watching Mercury" above a name field still reading "Mercury".) Only
|
|
23
|
-
// steps 1-
|
|
23
|
+
// steps 1-8 are wizard steps; anything else is the done panel's own bookkeeping.
|
|
24
24
|
//
|
|
25
25
|
// Harness: there is no jsdom in this repo, so this follows tests/wizard_draft_resume.mjs
|
|
26
26
|
// and tests/project_door_ui.mjs — slice the real region out of the page's INLINE script
|
|
@@ -57,8 +57,8 @@ test('both slices are still findable (a rename must fail loudly, not silently pa
|
|
|
57
57
|
assert.ok(offerSlice, 'paintResumeOffer() was not found in projects.html');
|
|
58
58
|
assert.match(enterSlice, /if \(focus && !resumeIntent\) \{/, 'the intent branch is part of the slice');
|
|
59
59
|
assert.match(enterSlice, /startProgress\(created\.id\);/, 'so is the resume branch it guards');
|
|
60
|
-
assert.match(enterSlice, /draft\.step >= 1 && draft\.step <=
|
|
61
|
-
'the STORED draft\'s step decides, not state.step — state.step is
|
|
60
|
+
assert.match(enterSlice, /draft\.step >= 1 && draft\.step <= 8/,
|
|
61
|
+
'the STORED draft\'s step decides, not state.step — state.step is 9 for the whole life of a finished standup');
|
|
62
62
|
assert.match(SCRIPT, /if \(created && created\.id\) \{ enterCreated\(created, !!focus\); return; \}/,
|
|
63
63
|
'enter() still hands the created record straight to it');
|
|
64
64
|
});
|
|
@@ -72,7 +72,7 @@ function runEnter({ focus, resumeIntent = false, draft = null, record }) {
|
|
|
72
72
|
const calls = { show: [], resets: 0, offer: [], done: 0, progress: [], polls: 0, focused: 0 };
|
|
73
73
|
// state.step is the DONE step for the whole life of a finished standup — the value
|
|
74
74
|
// a draft-blind reading would have trusted
|
|
75
|
-
const state = { step: draft ? draft.step :
|
|
75
|
+
const state = { step: draft ? draft.step : 9, name: 'Mercury', slug: 'mercury', domain: '', mode: 'greenfield', noAddress: false, modules: null };
|
|
76
76
|
const sandbox = {
|
|
77
77
|
record, focus, resumeIntent, state,
|
|
78
78
|
DRAFT_KEY: 'draft',
|
|
@@ -98,7 +98,7 @@ const REC = { id: 'inst-1', name: 'Acme', slug: 'acme', domain: 'acme.example',
|
|
|
98
98
|
|
|
99
99
|
test('a reload / Back / identity settle (focus false) still resumes the done panel, watch and all', () => {
|
|
100
100
|
const { calls, state } = runEnter({ focus: false, record: REC });
|
|
101
|
-
assert.deepEqual(calls.show, [[
|
|
101
|
+
assert.deepEqual(calls.show, [[9, false]], 'the done panel, and no focus steal on a non-deliberate arrival');
|
|
102
102
|
assert.equal(calls.resets, 0, 'nothing is reset — this is the same standup continuing');
|
|
103
103
|
assert.equal(calls.done, 1, 'the checklist is re-rendered');
|
|
104
104
|
assert.deepEqual(calls.progress, ['inst-1'], 'the progress watch is restarted from the record');
|
|
@@ -126,7 +126,7 @@ test('…and the standup stays one click away, named, on the resume link', () =>
|
|
|
126
126
|
|
|
127
127
|
test('the resume link gets the done panel back — the record was never cleared', () => {
|
|
128
128
|
const { calls, sandbox } = runEnter({ focus: true, resumeIntent: true, record: REC });
|
|
129
|
-
assert.deepEqual(calls.show, [[
|
|
129
|
+
assert.deepEqual(calls.show, [[9, true]], 'a deliberate resume moves focus to the done panel');
|
|
130
130
|
assert.deepEqual(calls.progress, ['inst-1'], 'the watch comes back with it');
|
|
131
131
|
assert.equal(sandbox.resumeIntent, false, 'the flag is ONE-SHOT — the next plain nav click must mean "new" again');
|
|
132
132
|
assert.deepEqual(calls.offer, [null], 'and the offer comes down behind it');
|
|
@@ -144,17 +144,28 @@ test('a second draft already under way is NOT wiped by navigating back to the wi
|
|
|
144
144
|
});
|
|
145
145
|
|
|
146
146
|
test('the done panel\'s own saved step is NOT a second draft — it must not block the reset', () => {
|
|
147
|
-
// Caught in a render, not in a unit test: show(
|
|
147
|
+
// Caught in a render, not in a unit test: show(9) calls saveDraft(), so DRAFT_KEY is
|
|
148
148
|
// non-empty again the moment a create lands. Reading "is there a draft?" left the
|
|
149
149
|
// finished project's name in the fresh form, under a line offering to resume it.
|
|
150
|
-
const { calls, state } = runEnter({ focus: true, record: REC, draft: { step:
|
|
150
|
+
const { calls, state } = runEnter({ focus: true, record: REC, draft: { step: 9, name: 'Mercury' } });
|
|
151
151
|
assert.equal(calls.resets, 1, 'the done step is bookkeeping, not a project being typed');
|
|
152
152
|
assert.deepEqual(calls.show, [[1, false]], 'out-of-range steps land on the name step');
|
|
153
153
|
assert.equal(state.name, '', 'and the finished project\'s name does not survive into it');
|
|
154
154
|
assert.equal(calls.done, 0, 'never back onto the done panel');
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
-
test('a draft
|
|
157
|
+
test('a draft on the app step or the review is live too — the last two numbered steps (task 1004351)', () => {
|
|
158
|
+
// The app step moved the review from 7 to 8. A guard left at 7 would reset a second
|
|
159
|
+
// project sitting on its review, silently wiping it.
|
|
160
|
+
for (const step of [7, 8]) {
|
|
161
|
+
const { calls, state } = runEnter({ focus: true, record: REC, draft: { step } });
|
|
162
|
+
assert.equal(calls.resets, 0, `step ${step} is a live draft`);
|
|
163
|
+
assert.deepEqual(calls.show, [[step, true]]);
|
|
164
|
+
assert.equal(state.step, step);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('a draft whose step is missing or junk resets too — only 1-8 preserves anything', () => {
|
|
158
169
|
for (const draft of [{ step: 0 }, { step: 9 }, { name: 'half-typed' }]) {
|
|
159
170
|
const { calls } = runEnter({ focus: true, record: REC, draft });
|
|
160
171
|
assert.equal(calls.resets, 1, `step ${JSON.stringify(draft.step)} is not a wizard step`);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// tests/wizard_preselect_why.mjs — the module picker always says WHY the preselect is on
|
|
2
2
|
// (task 1003684).
|
|
3
3
|
//
|
|
4
|
-
// The bug as filed said the starter-bundle "why" sentence was being dropped for
|
|
5
|
-
//
|
|
6
|
-
// changing anything claims nothing (ADR 0243).
|
|
7
|
-
// ['
|
|
8
|
-
// nothing, and correctly reports []
|
|
9
|
-
// byTeamShape
|
|
4
|
+
// The bug as filed said the starter-bundle "why" sentence was being dropped for one
|
|
5
|
+
// type + team-shape pair. It was not: `adjustments` is a DELTA, and a rule that fires
|
|
6
|
+
// without changing anything claims nothing (ADR 0243). When a type's bundle already holds
|
|
7
|
+
// the module a rule adds — today the game bundle is ['discord'] and the community rule
|
|
8
|
+
// ADDS discord — the rule fires, moves nothing, and correctly reports []:
|
|
9
|
+
// byTeamShape.community => {"bundle":["discord"],"adjustments":[]}
|
|
10
10
|
//
|
|
11
11
|
// The real defect is what the owner then saw: two extras pre-switched-on with no reason
|
|
12
12
|
// beside them — precisely the state the paint site's own comment calls out, "a preselect an
|
|
@@ -46,13 +46,13 @@ const BUNDLES = {
|
|
|
46
46
|
type: 'game',
|
|
47
47
|
label: 'Game starter',
|
|
48
48
|
summary: 'Somewhere for players to gather, and a place to walk a change through before anyone plays it.',
|
|
49
|
-
bundle: ['
|
|
49
|
+
bundle: ['discord'],
|
|
50
50
|
byTeamShape: {
|
|
51
51
|
// the live case: the rule fires, moves nothing, reports nothing
|
|
52
|
-
|
|
52
|
+
community: { bundle: ['discord'], adjustments: [] },
|
|
53
53
|
solo: {
|
|
54
|
-
bundle: [
|
|
55
|
-
adjustments: [{ reason: 'You are building this alone, so the
|
|
54
|
+
bundle: [],
|
|
55
|
+
adjustments: [{ reason: 'You are building this alone, so the community space is off.' }],
|
|
56
56
|
},
|
|
57
57
|
},
|
|
58
58
|
},
|
|
@@ -62,9 +62,9 @@ const BUNDLES = {
|
|
|
62
62
|
summary: 'Extra agents to run the reading and the experiments the question needs.',
|
|
63
63
|
bundle: ['agents'],
|
|
64
64
|
byTeamShape: {
|
|
65
|
-
|
|
66
|
-
bundle: ['agents', '
|
|
67
|
-
adjustments: [{ reason: '
|
|
65
|
+
community: {
|
|
66
|
+
bundle: ['agents', 'discord'],
|
|
67
|
+
adjustments: [{ reason: 'You said an open community is building this, so somewhere for it to gather is switched on.' }],
|
|
68
68
|
},
|
|
69
69
|
},
|
|
70
70
|
},
|
|
@@ -83,16 +83,16 @@ function why({ projType, teamShape, modules = null, bundles = BUNDLES }) {
|
|
|
83
83
|
return Array.from(sandbox.__out);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
test('game +
|
|
86
|
+
test('game + community: no delta, so the type\'s own summary is the reason', () => {
|
|
87
87
|
// This is the exact combination from the live walk that surfaced the bug.
|
|
88
|
-
const out = why({ projType: 'game', teamShape: '
|
|
88
|
+
const out = why({ projType: 'game', teamShape: 'community' });
|
|
89
89
|
assert.deepEqual(out, ['Somewhere for players to gather, and a place to walk a change through before anyone plays it.']);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
92
|
test('a REAL adjustment still speaks for itself — the summary does not displace it', () => {
|
|
93
|
-
const out = why({ projType: 'research', teamShape: '
|
|
93
|
+
const out = why({ projType: 'research', teamShape: 'community' });
|
|
94
94
|
assert.equal(out.length, 1);
|
|
95
|
-
assert.match(out[0], /
|
|
95
|
+
assert.match(out[0], /somewhere for it to gather/);
|
|
96
96
|
assert.doesNotMatch(out[0], /reading and the experiments/, 'the rule\'s sentence wins, not the summary');
|
|
97
97
|
});
|
|
98
98
|
|
|
@@ -128,7 +128,7 @@ test('before the bundles read lands, nothing is claimed', () => {
|
|
|
128
128
|
|
|
129
129
|
test('every sentence returned is a plain string, so the paint site can escape it directly', () => {
|
|
130
130
|
for (const t of ['game', 'research']) {
|
|
131
|
-
for (const s of ['
|
|
131
|
+
for (const s of ['community', 'solo', null]) {
|
|
132
132
|
for (const line of why({ projType: t, teamShape: s })) {
|
|
133
133
|
assert.equal(typeof line, 'string', `${t}/${s} yielded a non-string`);
|
|
134
134
|
}
|