@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
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
// tests/docs_scrubber_damage.mjs — the secret-scrubber ate real prose in
|
|
2
|
-
// docs/recipes/managed-settings-remote-control.md (task 1004027, part 4 of the
|
|
3
|
-
// damage family after 1004021 / 1004022).
|
|
4
|
-
//
|
|
5
|
-
// Two distinct repairs, and the SECOND is the trap. A scrubber pass replaced
|
|
6
|
-
// two pieces of ordinary content with the literal `<redacted>`:
|
|
7
|
-
// - an in-page anchor target, so the link went nowhere;
|
|
8
|
-
// - an `infra/*.sh` script name, so the doc pointed at no script at all.
|
|
9
|
-
// But four OTHER `<redacted>` occurrences in the same file are DELIBERATE — they
|
|
10
|
-
// stand in for real secrets (a tunnel credential, two tokens, a trust seed).
|
|
11
|
-
// A global sweep "fixes" the two and destroys the four, and nothing in the repo
|
|
12
|
-
// would notice, because the anchor lint only ever looks at link targets.
|
|
13
|
-
//
|
|
14
|
-
// So this file pins BOTH halves: the anchor and the script path resolve, AND the
|
|
15
|
-
// placeholder count stays at exactly four. The second assertion is the one that
|
|
16
|
-
// makes a future sweep fail loudly instead of quietly.
|
|
17
|
-
import assert from 'node:assert/strict';
|
|
18
|
-
import { test } from 'node:test';
|
|
19
|
-
import { createRequire } from 'node:module';
|
|
20
|
-
import { fileURLToPath } from 'node:url';
|
|
21
|
-
import fs from 'node:fs';
|
|
22
|
-
import path from 'node:path';
|
|
23
|
-
|
|
24
|
-
const require = createRequire(import.meta.url);
|
|
25
|
-
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
26
|
-
const { parseFile } = require(path.join(ROOT, 'scripts', 'gds', 'docs-entropy.js'));
|
|
27
|
-
|
|
28
|
-
const REL = 'docs/recipes/managed-settings-remote-control.md';
|
|
29
|
-
const ABS = path.join(ROOT, REL);
|
|
30
|
-
const raw = () => fs.readFileSync(ABS, 'utf8');
|
|
31
|
-
|
|
32
|
-
// The doc is only worth asserting against while it exists at this path.
|
|
33
|
-
test('the recipe under repair is still tracked at its documented path', () => {
|
|
34
|
-
assert.ok(fs.existsSync(ABS), `${REL} is missing — move this test with it`);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test('every in-page anchor in the recipe resolves to a heading it actually has', () => {
|
|
38
|
-
// The damaged link read `[named-tunnel automation](#<redacted>)`. Resolve
|
|
39
|
-
// through the SAME parser docs-entropy.js uses, so the slug this asserts is
|
|
40
|
-
// the slug the lint computes — a test with its own slug rule would let the
|
|
41
|
-
// doc pass here and still red the ratchet.
|
|
42
|
-
const { headings, links } = parseFile(ABS);
|
|
43
|
-
const slugs = new Set(headings.map((h) => h.slug));
|
|
44
|
-
const inPage = links.filter((l) => l.href.startsWith('#'));
|
|
45
|
-
|
|
46
|
-
assert.ok(inPage.length > 0, 'fixture sanity: the recipe does link to its own sections');
|
|
47
|
-
const broken = inPage.filter((l) => !slugs.has(l.href.slice(1)));
|
|
48
|
-
assert.deepEqual(
|
|
49
|
-
broken.map((l) => `${l.line}: ${l.href}`), [],
|
|
50
|
-
'an in-page anchor points at no heading in this file — this is what doc_broken_anchors counts',
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('the key-sync cron names a script the cloud-init renderer actually installs', () => {
|
|
55
|
-
// The damaged line read `infra/<redacted>.sh`. The authority for the real name
|
|
56
|
-
// is not prose anywhere: it is the renderer that bakes the script into the
|
|
57
|
-
// cloud-init, so assert the doc against THAT rather than against a literal.
|
|
58
|
-
const line = raw().split(/\r?\n/).find((l) => l.includes('GET /box/authorized-keys'))
|
|
59
|
-
?? raw().split(/\r?\n/).find((l) => l.includes('keeps `authorized_keys` in sync'));
|
|
60
|
-
assert.ok(line, 'fixture sanity: the recipe still describes the authorized_keys sync cron');
|
|
61
|
-
|
|
62
|
-
const named = [...raw().matchAll(/`infra\/([a-z0-9-]+\.sh)`/g)].map((m) => m[1]);
|
|
63
|
-
assert.ok(
|
|
64
|
-
named.includes('box-authorized-keys-fetch.sh'),
|
|
65
|
-
`the recipe names no authorized-keys script; found ${JSON.stringify(named)}`,
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
// Scoped to the repaired name on purpose. Other `infra/*.sh` the recipe cites
|
|
69
|
-
// (box-connect.sh) are builder-side scripts absent from this checkout for an
|
|
70
|
-
// unrelated, pre-existing reason (idea 1000895) — asserting over all of them
|
|
71
|
-
// would red this test on a gap it is not about.
|
|
72
|
-
const renderer = fs.readFileSync(path.join(ROOT, 'scripts', 'gds', 'box-infra.js'), 'utf8');
|
|
73
|
-
assert.ok(
|
|
74
|
-
renderer.includes('box-authorized-keys-fetch.sh'),
|
|
75
|
-
'box-infra.js no longer bakes box-authorized-keys-fetch.sh into the cloud-init — '
|
|
76
|
-
+ 'the recipe now names a script nothing installs, so fix the doc to match the renderer',
|
|
77
|
-
);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test('the four deliberate <redacted> secret placeholders survive — a global sweep must fail here', () => {
|
|
81
|
-
// This is the anti-fix assertion. Each of these stands in for a real secret and
|
|
82
|
-
// is correct as written; "repairing" them is the damage, not the cure.
|
|
83
|
-
const lines = raw().split(/\r?\n/);
|
|
84
|
-
const placeholders = lines
|
|
85
|
-
.map((text, i) => ({ line: i + 1, text }))
|
|
86
|
-
.filter((l) => l.text.includes('<redacted>'));
|
|
87
|
-
|
|
88
|
-
assert.equal(
|
|
89
|
-
placeholders.length, 4,
|
|
90
|
-
'expected exactly the four secret placeholders, got:\n'
|
|
91
|
-
+ placeholders.map((p) => ` ${p.line}: ${p.text.trim().slice(0, 110)}`).join('\n'),
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
// Named by CONTEXT, not by line number, so ordinary edits above them don't red this.
|
|
95
|
-
const contexts = ['"credential"', 'BOX_TUNNEL_TOKEN', 'BOX_SOURCE_FULL_TOKEN', '/root/.claude.json'];
|
|
96
|
-
for (const ctx of contexts) {
|
|
97
|
-
assert.ok(
|
|
98
|
-
placeholders.some((p) => p.text.includes(ctx)),
|
|
99
|
-
`the deliberate placeholder for ${ctx} is gone — a <redacted> sweep took a real secret stand-in with it`,
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// And no placeholder may sit in a link target or a path again: those two shapes
|
|
104
|
-
// are exactly what the scrubber damaged.
|
|
105
|
-
for (const p of placeholders) {
|
|
106
|
-
assert.ok(
|
|
107
|
-
!/\(#<redacted>\)/.test(p.text) && !/infra\/<redacted>/.test(p.text),
|
|
108
|
-
`line ${p.line} carries scrubber damage, not a secret placeholder: ${p.text.trim().slice(0, 110)}`,
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
// tests/module_scope_active_claims.mjs
|
|
2
|
-
//
|
|
3
|
-
// Unit guard for db.moduleScopeForActiveClaims (goal 1000051 / R92, ADR 0148 §2) —
|
|
4
|
-
// the lifecycle-port resolver the dev-box source-access path (R93/R94) reads to
|
|
5
|
-
// derive a builder's TASK-scoped sparse-checkout: the union of module KEYS across
|
|
6
|
-
// the builder's active claims' goals (claims → tasks → goals.scope_modules).
|
|
7
|
-
//
|
|
8
|
-
// DB-free, same posture as goal_achievement.mjs: the function honours the #538
|
|
9
|
-
// deps.pool injection seam (ONLY when NODE_ENV==='test'), so a SQL-aware fake
|
|
10
|
-
// client drives it with no live Postgres. We assert the query SHAPE (joins
|
|
11
|
-
// claims→tasks→goals, unnests scope_modules, filters ACTIVE claims), the bound
|
|
12
|
-
// params, the rows→keys mapping, and the numeric-input guard. The actual
|
|
13
|
-
// set-union/dedup is Postgres's DISTINCT (exercised end-to-end once box-access is
|
|
14
|
-
// wired — R95); here we prove the port method's contract.
|
|
15
|
-
//
|
|
16
|
-
// Run: node tests/module_scope_active_claims.mjs
|
|
17
|
-
|
|
18
|
-
import { strict as assert } from 'node:assert';
|
|
19
|
-
import { createRequire } from 'node:module';
|
|
20
|
-
import { makeRunner, makeSqlAwareClient } from './helpers.mjs';
|
|
21
|
-
|
|
22
|
-
process.env.NODE_ENV = 'test';
|
|
23
|
-
|
|
24
|
-
const require = createRequire(import.meta.url);
|
|
25
|
-
const { moduleScopeForActiveClaims } = require('../modules/lifecycle/db.js');
|
|
26
|
-
|
|
27
|
-
const { test, summary } = makeRunner();
|
|
28
|
-
|
|
29
|
-
function fakePool(handler) {
|
|
30
|
-
const c = makeSqlAwareClient(handler);
|
|
31
|
-
return { query: (sql, params) => c._client.query(sql, params), connect: c.connect, queries: c._client.queries };
|
|
32
|
-
}
|
|
33
|
-
const norm = (s) => String(s).replace(/\s+/g, ' ').trim();
|
|
34
|
-
const find = (queries, re) => queries.find((q) => re.test(norm(q.sql)));
|
|
35
|
-
|
|
36
|
-
// The one read this function issues: claims → tasks → goals, unnest scope_modules,
|
|
37
|
-
// active claims only. The fake returns whatever module-key rows we hand it (the
|
|
38
|
-
// DISTINCT/ORDER BY is Postgres's job — here we just prove the plumbing).
|
|
39
|
-
function scopeHandler(rows) {
|
|
40
|
-
return (s) => {
|
|
41
|
-
if (/FROM claims c/.test(s) && /JOIN goals g/.test(s) && /unnest\(g\.scope_modules\)/.test(s)) {
|
|
42
|
-
return { rows };
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// =========================================================================
|
|
49
|
-
// Happy path — active claims put N goals' scope_modules in scope
|
|
50
|
-
// =========================================================================
|
|
51
|
-
await test('returns the module keys the active claims scope, in DB order', async () => {
|
|
52
|
-
const pool = fakePool(scopeHandler([{ module_key: 'dev-box' }, { module_key: 'kernel' }, { module_key: 'lifecycle' }]));
|
|
53
|
-
const keys = await moduleScopeForActiveClaims(3, { pool });
|
|
54
|
-
assert.deepEqual(keys, ['dev-box', 'kernel', 'lifecycle'], 'maps rows → module_key list');
|
|
55
|
-
|
|
56
|
-
const q = find(pool.queries, /FROM claims c JOIN tasks t .* JOIN goals g/);
|
|
57
|
-
assert.ok(q, 'issues the claims→tasks→goals scope read');
|
|
58
|
-
assert.match(norm(q.sql), /c\.builder_id = \$1 AND c\.released_at IS NULL/, 'active claims only, keyed on builder');
|
|
59
|
-
assert.match(norm(q.sql), /unnest\(g\.scope_modules\)/, 'unnests the goal scope_modules array');
|
|
60
|
-
assert.match(norm(q.sql), /SELECT DISTINCT/, 'DISTINCT dedupes the union across claims');
|
|
61
|
-
assert.deepEqual(q.params, [3], 'binds the builder id');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// =========================================================================
|
|
65
|
-
// No active claim (or no scoped goal) → base-only signal ([])
|
|
66
|
-
// =========================================================================
|
|
67
|
-
await test('no active claim / no scoped goal → empty array (box falls back to BASE-only)', async () => {
|
|
68
|
-
const pool = fakePool(scopeHandler([]));
|
|
69
|
-
const keys = await moduleScopeForActiveClaims(3, { pool });
|
|
70
|
-
assert.deepEqual(keys, [], 'zero rows → []');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// =========================================================================
|
|
74
|
-
// Input guard — a non-numeric builder id never reaches SQL
|
|
75
|
-
// =========================================================================
|
|
76
|
-
await test('non-numeric builderId throws before any query', async () => {
|
|
77
|
-
const pool = fakePool(scopeHandler([{ module_key: 'dev-box' }]));
|
|
78
|
-
await assert.rejects(() => moduleScopeForActiveClaims('not-a-number', { pool }), /must be numeric/);
|
|
79
|
-
await assert.rejects(() => moduleScopeForActiveClaims(NaN, { pool }), /must be numeric/);
|
|
80
|
-
assert.equal(pool.queries.length, 0, 'guard trips before the pool is touched');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// =========================================================================
|
|
84
|
-
// The #538 deps.pool test seam is actually wired (we ARE in NODE_ENV==='test')
|
|
85
|
-
// =========================================================================
|
|
86
|
-
await test('honours the deps.pool test seam', async () => {
|
|
87
|
-
let touched = false;
|
|
88
|
-
const pool = fakePool((s) => { touched = true; return /unnest/.test(s) ? { rows: [{ module_key: 'game' }] } : null; });
|
|
89
|
-
const keys = await moduleScopeForActiveClaims(7, { pool });
|
|
90
|
-
assert.ok(touched, 'the injected pool was used');
|
|
91
|
-
assert.deepEqual(keys, ['game']);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
summary();
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
// tests/ship_api_client_pathspec.mjs
|
|
2
|
-
//
|
|
3
|
-
// Unit tests for task 1002417 — ship.js named a git path that could not exist,
|
|
4
|
-
// and it broke EVERY ship.
|
|
5
|
-
//
|
|
6
|
-
// regenerateApiClient() stages the regenerated typed client plus the devbox
|
|
7
|
-
// Electron app's vendored CJS copy. After the app moved from a top-level
|
|
8
|
-
// devbox-app/ to modules/dev-box/app/ (ADR 0145) the vendored path went stale.
|
|
9
|
-
// The failure was asymmetric and therefore silent for a long time:
|
|
10
|
-
//
|
|
11
|
-
// git status --porcelain <missing path> → exit 0 (tolerated, so no signal)
|
|
12
|
-
// git add <missing path> → exit 128 (aborts the WHOLE stage)
|
|
13
|
-
//
|
|
14
|
-
// Because the `git add` aborts, the freshly regenerated clients/bongos-client is
|
|
15
|
-
// never staged, the worktree stays dirty, and the ship's auto-merge then bails
|
|
16
|
-
// with "main worktree busy" — stranding the task at `confirmed` with no PR.
|
|
17
|
-
//
|
|
18
|
-
// apiClientPathspec is the pure rule behind that: never name a path `git add`
|
|
19
|
-
// cannot match. Proven here with an injected existsSync — no git, no fs.
|
|
20
|
-
//
|
|
21
|
-
// Run: node tests/ship_api_client_pathspec.mjs
|
|
22
|
-
|
|
23
|
-
import { strict as assert } from 'node:assert';
|
|
24
|
-
import { createRequire } from 'node:module';
|
|
25
|
-
import path from 'node:path';
|
|
26
|
-
|
|
27
|
-
const require = createRequire(import.meta.url);
|
|
28
|
-
const ship = require('../scripts/gds/ship.js');
|
|
29
|
-
const { apiClientPathspec, VENDORED_CLIENT_REL } = ship;
|
|
30
|
-
|
|
31
|
-
let passed = 0;
|
|
32
|
-
let failed = 0;
|
|
33
|
-
function test(name, fn) {
|
|
34
|
-
try {
|
|
35
|
-
fn();
|
|
36
|
-
passed++;
|
|
37
|
-
console.log(` ok ${name}`);
|
|
38
|
-
} catch (err) {
|
|
39
|
-
failed++;
|
|
40
|
-
console.log(` FAIL ${name}`);
|
|
41
|
-
console.log(` ${err.message}`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const ROOT = path.join('/repo');
|
|
46
|
-
const never = () => false;
|
|
47
|
-
const always = () => true;
|
|
48
|
-
|
|
49
|
-
test('the vendored copy lives inside the dev-box module, not at the old top level', () => {
|
|
50
|
-
assert.equal(VENDORED_CLIENT_REL, 'modules/dev-box/app/src/vendor/bongos-client.cjs');
|
|
51
|
-
assert.ok(!VENDORED_CLIENT_REL.startsWith('devbox-app/'), 'must not use the pre-ADR-0145 path');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('the generated client is always staged', () => {
|
|
55
|
-
assert.ok(apiClientPathspec({ worktreeRoot: ROOT, existsSync: never }).includes('clients/bongos-client'));
|
|
56
|
-
assert.ok(apiClientPathspec({ worktreeRoot: ROOT, existsSync: always }).includes('clients/bongos-client'));
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('vendored copy present → it is staged too', () => {
|
|
60
|
-
const p = apiClientPathspec({ worktreeRoot: ROOT, existsSync: always });
|
|
61
|
-
assert.deepEqual(p, ['clients/bongos-client', VENDORED_CLIENT_REL]);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// The regression. Before the fix this returned the vendored path unconditionally,
|
|
65
|
-
// so `git add` exited 128 and left the worktree dirty.
|
|
66
|
-
test('vendored copy ABSENT → it is omitted (git add would abort on it)', () => {
|
|
67
|
-
const p = apiClientPathspec({ worktreeRoot: ROOT, existsSync: never });
|
|
68
|
-
assert.deepEqual(p, ['clients/bongos-client']);
|
|
69
|
-
assert.ok(!p.some((x) => x.includes('vendor')), 'no unmatched pathspec may reach git add');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('existence is probed at the vendored path under the given worktree root', () => {
|
|
73
|
-
const seen = [];
|
|
74
|
-
apiClientPathspec({
|
|
75
|
-
worktreeRoot: ROOT,
|
|
76
|
-
existsSync: (p) => { seen.push(p); return false; },
|
|
77
|
-
});
|
|
78
|
-
assert.equal(seen.length, 1);
|
|
79
|
-
assert.equal(seen[0], path.join(ROOT, VENDORED_CLIENT_REL));
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// A ship can run from a linked worktree, where the main checkout is a different
|
|
83
|
-
// tree (the task-2060 rationale). The probe must follow the root it is handed.
|
|
84
|
-
test('probes the worktree it was handed, not a fixed root', () => {
|
|
85
|
-
const seen = [];
|
|
86
|
-
apiClientPathspec({
|
|
87
|
-
worktreeRoot: path.join('/linked-worktree'),
|
|
88
|
-
existsSync: (p) => { seen.push(p); return false; },
|
|
89
|
-
});
|
|
90
|
-
assert.equal(seen[0], path.join('/linked-worktree', VENDORED_CLIENT_REL));
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test('every returned entry is repo-relative (git pathspecs, not absolute paths)', () => {
|
|
94
|
-
for (const p of apiClientPathspec({ worktreeRoot: ROOT, existsSync: always })) {
|
|
95
|
-
assert.ok(!path.isAbsolute(p), `${p} must be repo-relative`);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
console.log(`\n${passed} passed, ${failed} failed`);
|
|
100
|
-
process.exit(failed === 0 ? 0 : 1);
|
package/tests/tree_preflight.mjs
DELETED
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
// tests/tree_preflight.mjs — the partial-checkout preflight (task 1003098).
|
|
2
|
-
//
|
|
3
|
-
// THE BUG. On a task-scoped dev box — a supported, server-driven configuration —
|
|
4
|
-
// `scripts/gds/ship.js` died with a raw stack trace naming ONE missing module. Fix
|
|
5
|
-
// that one, run again, learn the next. The filer lost a session to it, and their
|
|
6
|
-
// workaround (`git sparse-checkout disable` plus disabling the re-scope cron) is
|
|
7
|
-
// not something a builder should have to discover. Node's loader cannot do better:
|
|
8
|
-
// it stops at the first unresolvable specifier and never reaches the second.
|
|
9
|
-
//
|
|
10
|
-
// THE TWO FAILURE DIRECTIONS, and both are tested, because only one is obvious:
|
|
11
|
-
//
|
|
12
|
-
// under-reporting — the original bug: name one missing thing, hide the other six.
|
|
13
|
-
// OVER-reporting — the regression a naive fix causes: several of the CLI's
|
|
14
|
-
// module requires are deliberately try-guarded, so those
|
|
15
|
-
// commands RUN on a scoped box. Refusing them would tell a
|
|
16
|
-
// builder who could have shipped that they cannot, which is
|
|
17
|
-
// worse than the trace it replaces.
|
|
18
|
-
//
|
|
19
|
-
// Pure: a fake file system throughout, per the task's own DONE-WHEN. The cases at
|
|
20
|
-
// the end are the empirical anchor — they run the classifier against the REAL
|
|
21
|
-
// scripts/gds files and pin it to behaviour observed on a simulated scoped tree:
|
|
22
|
-
// start.js survives, claim.js dies on one module, ship.js on seven.
|
|
23
|
-
//
|
|
24
|
-
// Run: node tests/tree_preflight.mjs
|
|
25
|
-
|
|
26
|
-
import { strict as assert } from 'node:assert';
|
|
27
|
-
import { createRequire } from 'node:module';
|
|
28
|
-
import { fileURLToPath } from 'node:url';
|
|
29
|
-
import { dirname, join } from 'node:path';
|
|
30
|
-
|
|
31
|
-
const require = createRequire(import.meta.url);
|
|
32
|
-
const tp = require('../scripts/gds/tree-preflight.js');
|
|
33
|
-
|
|
34
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
35
|
-
const repoRoot = join(__dirname, '..');
|
|
36
|
-
|
|
37
|
-
let passed = 0;
|
|
38
|
-
let failed = 0;
|
|
39
|
-
const t = (name, fn) => {
|
|
40
|
-
try { fn(); passed += 1; console.log(` PASS ${name}`); }
|
|
41
|
-
catch (e) { failed += 1; console.log(` FAIL ${name}\n ${e.message}`); }
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// A fake scripts/gds tree. Keys are absolute-ish paths as the walker resolves them.
|
|
45
|
-
function fakeFs(files) {
|
|
46
|
-
return {
|
|
47
|
-
readFile: (f) => {
|
|
48
|
-
const key = String(f).replace(/\\/g, '/');
|
|
49
|
-
const hit = Object.keys(files).find((k) => key.endsWith(k));
|
|
50
|
-
if (hit === undefined) { const e = new Error('ENOENT'); e.code = 'ENOENT'; throw e; }
|
|
51
|
-
return files[hit];
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
console.log('\nthe walk — what the command actually needs:');
|
|
57
|
-
|
|
58
|
-
t('collects a direct module require', () => {
|
|
59
|
-
const fs = fakeFs({ 'scripts/gds/a.js': "const x = require('../../modules/lifecycle/dead-deps.js');" });
|
|
60
|
-
assert.deepEqual(
|
|
61
|
-
tp.requiredModulePaths('/repo/scripts/gds/a.js', { ...fs, root: '/repo' }),
|
|
62
|
-
['modules/lifecycle/dead-deps.js'],
|
|
63
|
-
);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
t('FOLLOWS sibling requires — the whole reason a declared list would be wrong', () => {
|
|
67
|
-
// ship.js reaches its modules through sixteen ship-*.js phase files; a hand-kept
|
|
68
|
-
// list of "what ship.js needs" is a second copy of the require graph.
|
|
69
|
-
const fs = fakeFs({
|
|
70
|
-
'scripts/gds/entry.js': "const p = require('./phase.js');",
|
|
71
|
-
'scripts/gds/phase.js': "const g = require('../../modules/grading/grader.js');",
|
|
72
|
-
});
|
|
73
|
-
assert.deepEqual(
|
|
74
|
-
tp.requiredModulePaths('/repo/scripts/gds/entry.js', { ...fs, root: '/repo' }),
|
|
75
|
-
['modules/grading/grader.js'],
|
|
76
|
-
);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
t('a require cycle terminates', () => {
|
|
80
|
-
const fs = fakeFs({
|
|
81
|
-
'scripts/gds/a.js': "require('./b.js'); require('../../modules/lifecycle/one.js');",
|
|
82
|
-
'scripts/gds/b.js': "require('./a.js'); require('../../modules/lifecycle/two.js');",
|
|
83
|
-
});
|
|
84
|
-
assert.deepEqual(
|
|
85
|
-
tp.requiredModulePaths('/repo/scripts/gds/a.js', { ...fs, root: '/repo' }),
|
|
86
|
-
['modules/lifecycle/one.js', 'modules/lifecycle/two.js'],
|
|
87
|
-
);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
t('an unreadable file is skipped, never thrown on', () => {
|
|
91
|
-
// This runs on a checkout that is BY DEFINITION incomplete. A preflight that
|
|
92
|
-
// crashes while diagnosing a partial tree is worse than the trace it replaces.
|
|
93
|
-
const fs = fakeFs({ 'scripts/gds/a.js': "require('./gone.js'); require('../../modules/lifecycle/one.js');" });
|
|
94
|
-
assert.deepEqual(
|
|
95
|
-
tp.requiredModulePaths('/repo/scripts/gds/a.js', { ...fs, root: '/repo' }),
|
|
96
|
-
['modules/lifecycle/one.js'],
|
|
97
|
-
);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
t('a bare specifier gains .js; one that names an extension is left alone', () => {
|
|
101
|
-
const fs = fakeFs({
|
|
102
|
-
'scripts/gds/a.js': "require('../../modules/lifecycle/ship-preflight');require('../../modules/lifecycle/d.json');",
|
|
103
|
-
});
|
|
104
|
-
assert.deepEqual(
|
|
105
|
-
tp.requiredModulePaths('/repo/scripts/gds/a.js', { ...fs, root: '/repo' }),
|
|
106
|
-
['modules/lifecycle/d.json', 'modules/lifecycle/ship-preflight.js'],
|
|
107
|
-
);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
console.log('\nOVER-reporting — the regression a naive fix causes:');
|
|
111
|
-
|
|
112
|
-
t('a try-guarded require is NOT a requirement', () => {
|
|
113
|
-
// context-pack.js and cli-lib.js both guard theirs and fall back honestly, which
|
|
114
|
-
// is why /builder-start works on a scoped box today. Reporting them would refuse
|
|
115
|
-
// a command that runs.
|
|
116
|
-
const fs = fakeFs({
|
|
117
|
-
'scripts/gds/a.js': "let c=null; try { c = require('../../modules/lifecycle/task-classifier'); } catch (_) {}",
|
|
118
|
-
});
|
|
119
|
-
assert.deepEqual(tp.requiredModulePaths('/repo/scripts/gds/a.js', { ...fs, root: '/repo' }), []);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
t('a require AFTER a closed try is still a requirement', () => {
|
|
123
|
-
// The brace walk must pop. If it did not, one guarded require near the top would
|
|
124
|
-
// silently excuse every require below it in the file.
|
|
125
|
-
const fs = fakeFs({
|
|
126
|
-
'scripts/gds/a.js': "try { require('../../modules/lifecycle/opt.js'); } catch (_) {}\nrequire('../../modules/lifecycle/need.js');",
|
|
127
|
-
});
|
|
128
|
-
assert.deepEqual(
|
|
129
|
-
tp.requiredModulePaths('/repo/scripts/gds/a.js', { ...fs, root: '/repo' }),
|
|
130
|
-
['modules/lifecycle/need.js'],
|
|
131
|
-
);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
t('a require quoted inside a COMMENT is not walked', () => {
|
|
135
|
-
// tree-preflight.js's own header quotes the failing require verbatim to explain
|
|
136
|
-
// the bug. A raw-text scan would read that prose as a live edge — the defect
|
|
137
|
-
// filed as task 1003832 against a test that scanned raw source.
|
|
138
|
-
const fs = fakeFs({
|
|
139
|
-
'scripts/gds/a.js': "// require('../../modules/grading/nope.js')\n/* require('../../modules/grading/also.js') */\nrequire('../../modules/lifecycle/real.js');",
|
|
140
|
-
});
|
|
141
|
-
assert.deepEqual(
|
|
142
|
-
tp.requiredModulePaths('/repo/scripts/gds/a.js', { ...fs, root: '/repo' }),
|
|
143
|
-
['modules/lifecycle/real.js'],
|
|
144
|
-
);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
console.log('\nthe message — one run, every missing thing:');
|
|
148
|
-
|
|
149
|
-
t('nothing missing → null, so a complete checkout is a silent no-op', () => {
|
|
150
|
-
assert.equal(tp.preflightMessage([], { command: '/builder-ship' }), null);
|
|
151
|
-
assert.equal(tp.preflightMessage(null, { command: '/builder-ship' }), null);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
t('EVERY missing requirement is listed, not the first', () => {
|
|
155
|
-
// The whole ask. Seven for ship.js is seven guess-read-fix cycles avoided.
|
|
156
|
-
const missing = ['modules/a/one.js', 'modules/b/two.js', 'modules/b/three.js'];
|
|
157
|
-
const msg = tp.preflightMessage(missing, { command: '/builder-ship' });
|
|
158
|
-
for (const m of missing) assert.ok(msg.includes(m), `names ${m}`);
|
|
159
|
-
assert.match(msg, /Missing \(3\)/, 'and says how many');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
t('it names the CONDITION and BOTH remedies', () => {
|
|
163
|
-
const msg = tp.preflightMessage(['modules/a/one.js'], { command: '/builder-ship' });
|
|
164
|
-
assert.match(msg, /task-scoped/i, 'names why the code is absent');
|
|
165
|
-
assert.match(msg, /not a broken install/i, 'and rules out the wrong diagnosis');
|
|
166
|
-
assert.match(msg, /box-sync\.js/, 're-scope');
|
|
167
|
-
assert.match(msg, /sparse-checkout disable/, 'or take the whole tree');
|
|
168
|
-
assert.match(msg, /\/builder-ship/, 'and says which command was refused');
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
t('it rolls the missing files up to whole directories', () => {
|
|
172
|
-
const msg = tp.preflightMessage(['modules/lifecycle/a.js', 'modules/lifecycle/b.js', 'modules/grading/c.js']);
|
|
173
|
-
assert.match(msg, /modules\/grading, modules\/lifecycle/, 'deduped and sorted');
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
console.log('\ncheckTree — what the entry points call:');
|
|
177
|
-
|
|
178
|
-
t('a complete tree returns null', () => {
|
|
179
|
-
const fs = fakeFs({ 'scripts/gds/a.js': "require('../../modules/lifecycle/one.js');" });
|
|
180
|
-
assert.equal(tp.checkTree('/repo/scripts/gds/a.js', { ...fs, root: '/repo', exists: () => true }), null);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
t('a partial tree returns the message', () => {
|
|
184
|
-
const fs = fakeFs({ 'scripts/gds/a.js': "require('../../modules/lifecycle/one.js');" });
|
|
185
|
-
const msg = tp.checkTree('/repo/scripts/gds/a.js', { ...fs, root: '/repo', exists: () => false, command: '/builder-x' });
|
|
186
|
-
assert.match(msg, /modules\/lifecycle\/one\.js/);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
t('a preflight that THROWS returns null rather than replacing one bad trace with another', () => {
|
|
190
|
-
const boom = { readFile: () => { throw new Error('boom'); }, exists: () => { throw new Error('boom'); } };
|
|
191
|
-
assert.equal(tp.checkTree('/repo/scripts/gds/a.js', { ...boom, root: '/repo' }), null);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
console.log('\nthe empirical anchor — the REAL CLI files:');
|
|
195
|
-
|
|
196
|
-
t('start.js needs NO unguarded module code, matching that it runs on a scoped box', () => {
|
|
197
|
-
// Observed: with modules/ hidden, start.js prints its card normally. If this
|
|
198
|
-
// list is ever non-empty, /builder-start starts refusing on a box where it works.
|
|
199
|
-
const req = tp.requiredModulePaths(join(repoRoot, 'scripts/gds/start.js'), { root: repoRoot });
|
|
200
|
-
assert.deepEqual(req, [], `start.js must survive a scoped box; got ${req.join(', ')}`);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
t('claim.js and ship.js DO need module code, matching the traces they died with', () => {
|
|
204
|
-
const claim = tp.requiredModulePaths(join(repoRoot, 'scripts/gds/claim.js'), { root: repoRoot });
|
|
205
|
-
assert.ok(claim.includes('modules/lifecycle/dead-deps.js'),
|
|
206
|
-
'the exact module claim.js died on');
|
|
207
|
-
const ship = tp.requiredModulePaths(join(repoRoot, 'scripts/gds/ship.js'), { root: repoRoot });
|
|
208
|
-
assert.ok(ship.includes('modules/lifecycle/ship-preflight.js'),
|
|
209
|
-
'the exact module ship.js died on');
|
|
210
|
-
assert.ok(ship.length >= 5,
|
|
211
|
-
`ship.js fans out across phase files; the walk found ${ship.length}, so the one-per-run cost was real`);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
t('the three entry points actually CALL the preflight, above their first require', () => {
|
|
215
|
-
// The check is worthless if it runs after the require that dies. Asserted by
|
|
216
|
-
// position, because that is the property — not merely that the call exists.
|
|
217
|
-
for (const f of ['start.js', 'claim.js', 'ship.js']) {
|
|
218
|
-
const src = require('node:fs').readFileSync(join(repoRoot, 'scripts/gds', f), 'utf8');
|
|
219
|
-
const call = src.indexOf('_checkTree(__filename');
|
|
220
|
-
assert.ok(call > 0, `${f} calls the preflight`);
|
|
221
|
-
const firstModuleRequire = src.search(/require\(\s*['"]\.\.\/\.\.\/modules\//);
|
|
222
|
-
if (firstModuleRequire > 0) {
|
|
223
|
-
assert.ok(call < firstModuleRequire, `${f}: the preflight must run BEFORE the first modules/ require`);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
t('the preflight names the command for the READER, never a hard-coded slash command', () => {
|
|
229
|
-
// The message this preflight prints is reader-facing, and `/builder-ship` does not exist
|
|
230
|
-
// outside Claude Code — a person running the public `bongos` CLI would be handed a command
|
|
231
|
-
// they cannot type. tests/cli_surface.mjs scans start.js for exactly this, but nothing
|
|
232
|
-
// scanned claim.js or ship.js, and the first draft of this task hard-coded all three.
|
|
233
|
-
for (const f of ['start.js', 'claim.js', 'ship.js']) {
|
|
234
|
-
const src = require('node:fs').readFileSync(join(repoRoot, 'scripts/gds', f), 'utf8');
|
|
235
|
-
const line = src.split('\n').find((l) => l.includes('_checkTree(__filename'));
|
|
236
|
-
assert.ok(line, `${f} calls the preflight`);
|
|
237
|
-
assert.ok(!/\/builder-/.test(line), `${f}: hard-coded slash command in the preflight message`);
|
|
238
|
-
assert.match(line, /_surfaceAction\(/, `${f}: the command name must come from the surface switch`);
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
console.log(`\ntree_preflight: ${passed} passed, ${failed} failed`);
|
|
243
|
-
if (failed) process.exit(1);
|