@bongos/core 1.19.1073 → 1.19.1074
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 +320 -465
- package/.claude/skills/feedback/SKILL.md +2 -2
- 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 +171 -1661
- 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 +2 -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 +9 -9
- 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/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/lifecycle/db-claim-reads.js +1 -58
- package/modules/lifecycle/db-ship.js +0 -5
- package/modules/lifecycle/db.js +0 -4
- 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/routes/tasks.js +5 -7
- package/modules/memory/module.json +1 -0
- 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 +2 -2
- package/modules/platform-identity/tests/platform-identity.mjs +17 -3
- package/modules/provisioning/module.json +1 -0
- package/modules/provisioning/provisioning.js +2 -2
- 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/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 +78 -0
- package/scripts/gds/artifact-format.js +203 -0
- package/scripts/gds/audit-authorship.sh +1 -13
- package/scripts/gds/claim.js +0 -15
- package/scripts/gds/cli-lib.js +8 -8
- 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 +1 -1
- package/scripts/gds/feedback-latest.js +139 -9
- package/scripts/gds/fitness-checks-identity.js +2 -7
- package/scripts/gds/fitness-checks-write-validation.js +4 -0
- package/scripts/gds/fitness-ratchets.js +4 -4
- package/scripts/gds/fitness.js +10 -10
- package/scripts/gds/gen-diagrams.js +49 -71
- package/scripts/gds/http-api-client.js +12 -19
- package/scripts/gds/init.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/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/regen-instance-docs.js +1 -1
- package/scripts/gds/run-unit-tests.js +2 -16
- package/scripts/gds/sandbox-stage.js +39 -341
- package/scripts/gds/ship-flow.js +6 -6
- package/scripts/gds/ship-land.js +3 -4
- package/scripts/gds/ship-regen.js +10 -41
- package/scripts/gds/ship.js +4 -24
- 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/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/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 +6 -22
- package/src/bongos/routes/modules.js +83 -0
- package/src/bongos/routes.js +5 -0
- 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 +15 -7
- package/src/module-seams.js +2 -3
- package/src/modules.js +42 -14
- package/tests/agents_spend_guard.mjs +1 -1
- package/tests/api_client.mjs +0 -15
- package/tests/auth_page_gate.mjs +10 -10
- 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/consumer_layout_boot.mjs +2 -0
- package/tests/copy_desk_page_docx.mjs +33 -2
- package/tests/core_upgrade_runner.mjs +6 -6
- package/tests/deploy_divergence_line.mjs +11 -11
- 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/gate_approvals.mjs +0 -1
- package/tests/go_live.mjs +3 -3
- package/tests/government_abuse_matrix.mjs +20 -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_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/host_topology_skips.mjs +1 -2
- 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/module-scope-map.mjs +9 -9
- package/tests/module_catalog.mjs +1 -1
- package/tests/module_cli.mjs +15 -15
- package/tests/module_contributions.mjs +6 -6
- package/tests/module_loader.mjs +6 -6
- package/tests/module_manifest.mjs +13 -4
- 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/onboarding_route_signals.mjs +21 -31
- package/tests/onboarding_state.mjs +35 -49
- package/tests/permission_path.mjs +0 -3
- package/tests/platform_boot.mjs +1 -2
- package/tests/profile_route.mjs +1 -6
- package/tests/projects_hub.mjs +5 -5
- package/tests/projects_hub_app_step.mjs +138 -0
- package/tests/projects_hub_module_picker.mjs +2 -2
- package/tests/projects_hub_pre_uat.mjs +4 -5
- package/tests/provision.mjs +7 -7
- package/tests/provisioning_starter_bundles.mjs +0 -22
- package/tests/publish_branch_route.mjs +10 -18
- package/tests/publish_manifest.mjs +1 -2
- package/tests/rank_tier_single_source.mjs +1 -13
- package/tests/repo_map.mjs +3 -4
- package/tests/runner_drift.mjs +3 -21
- package/tests/sandbox_stage.mjs +57 -390
- package/tests/session_rename_fallback.mjs +0 -21
- package/tests/ship_error_shape.mjs +1 -1
- package/tests/task_visual_slots.mjs +31 -0
- package/tests/terms_acceptance.mjs +1 -1
- 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
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// tests/projects_hub_app_step.mjs — the create wizard's "where your app runs" step,
|
|
2
|
+
// EXECUTED (task 1004351, ADR 0345).
|
|
3
|
+
//
|
|
4
|
+
// The step is a native radio group, so almost all of its behaviour is one rule: the
|
|
5
|
+
// review may only ever name a place the owner could actually pick. Render is listed
|
|
6
|
+
// but disabled until the connect flow lands (task 1004352), and "decide later" is the
|
|
7
|
+
// owner's ruling for the default. A static regex cannot tell a guard that works from
|
|
8
|
+
// one that is never consulted, so — as tests/projects_hub_module_picker.mjs does for
|
|
9
|
+
// the picker — this slices the block out of projects.html by its own sentinels and
|
|
10
|
+
// runs it in `vm` under a small DOM stub.
|
|
11
|
+
//
|
|
12
|
+
// Run: node --test --test-reporter=tap tests/projects_hub_app_step.mjs
|
|
13
|
+
|
|
14
|
+
import test from 'node:test';
|
|
15
|
+
import assert from 'node:assert/strict';
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import vm from 'node:vm';
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
|
|
21
|
+
const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
22
|
+
const HUB = fs.readFileSync(path.join(ROOT, 'modules', 'public-landing', 'public', 'projects.html'), 'utf8').replace(/\r\n/g, '\n');
|
|
23
|
+
|
|
24
|
+
const START = '/* the app step (task 1004351)';
|
|
25
|
+
const END = ' function renderReview() {';
|
|
26
|
+
const from = HUB.indexOf(START);
|
|
27
|
+
const to = HUB.indexOf(END, from);
|
|
28
|
+
assert.ok(from > -1, 'the app-step block opens with its sentinel comment');
|
|
29
|
+
assert.ok(to > from, 'the app-step block ends where renderReview begins');
|
|
30
|
+
const BLOCK = HUB.slice(from, to);
|
|
31
|
+
|
|
32
|
+
const PANEL = (HUB.match(/<section class="panel" id="panel-app"[\s\S]*?<\/section>/) || [''])[0];
|
|
33
|
+
|
|
34
|
+
// Two radios, built from the real markup so the harness cannot drift from the page.
|
|
35
|
+
function radiosFromMarkup(renderEnabled) {
|
|
36
|
+
return [...PANEL.matchAll(/<input type="radio" name="appPlatform" value="([a-z-]+)"([^>]*)>/g)].map((m) => ({
|
|
37
|
+
name: 'appPlatform',
|
|
38
|
+
value: m[1],
|
|
39
|
+
disabled: m[1] === 'render' ? !renderEnabled && /\sdisabled/.test(m[2]) : /\sdisabled/.test(m[2]),
|
|
40
|
+
checked: /\schecked/.test(m[2]),
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function load({ draft = {}, renderEnabled = false } = {}) {
|
|
45
|
+
const inputs = radiosFromMarkup(renderEnabled);
|
|
46
|
+
let changeHandler = null;
|
|
47
|
+
const saves = [];
|
|
48
|
+
const root = {
|
|
49
|
+
querySelector(sel) {
|
|
50
|
+
if (sel === '#appOpts') return { addEventListener: (ev, fn) => { if (ev === 'change') changeHandler = fn; } };
|
|
51
|
+
throw new Error('unexpected selector ' + sel);
|
|
52
|
+
},
|
|
53
|
+
querySelectorAll(sel) {
|
|
54
|
+
assert.equal(sel, '#appOpts input[name=appPlatform]');
|
|
55
|
+
return inputs;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const state = Object.assign({ appPlatform: 'later' }, draft);
|
|
59
|
+
const ctx = { root, state, saveDraft: () => saves.push(state.appPlatform) };
|
|
60
|
+
vm.createContext(ctx);
|
|
61
|
+
vm.runInContext(BLOCK + '\nthis.api = { appPlatformChoice, syncAppStep, appRow };', ctx);
|
|
62
|
+
return { ...ctx.api, inputs, state, saves, change: (value) => changeHandler({ target: inputs.find((i) => i.value === value) }) };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
test('the panel is real markup: Render listed but not pickable yet, Decide later the default', () => {
|
|
66
|
+
assert.ok(PANEL, '#panel-app exists');
|
|
67
|
+
assert.match(PANEL, /<input type="radio" name="appPlatform" value="render" disabled/, 'Render cannot be picked until task 1004352');
|
|
68
|
+
assert.match(PANEL, /<span class="soonTag">Coming soon<\/span>/, 'and says so in words, not by colour alone');
|
|
69
|
+
assert.match(PANEL, /<input type="radio" name="appPlatform" value="later" checked>/, 'Decide later is the default');
|
|
70
|
+
assert.match(PANEL, /We host your Builders' hall/, 'the step says the hall is ours to host (ADR 0345 D1), in the page\'s one spelling of it');
|
|
71
|
+
assert.match(PANEL, /<form data-next="8">/, 'Next leads to the review');
|
|
72
|
+
assert.match(PANEL, /data-back="6"/, 'Back returns to the address step');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('the step sits between Address and Review in the panels map and on the rail', () => {
|
|
76
|
+
assert.match(HUB, /6: 'panel-address', 7: 'panel-app', 8: 'panel-review', 9: 'panel-done'/);
|
|
77
|
+
assert.match(HUB, /<li data-step="7"><span class="snum">7<\/span><span class="slbl">App<\/span><\/li>/);
|
|
78
|
+
assert.match(HUB, /<li data-step="8"><span class="snum">8<\/span><span class="slbl">Create<\/span><\/li>/);
|
|
79
|
+
assert.match(HUB, /data-back="7">← Back<\/button>\n\s*<button type="button" class="btn big" id="doCreate">/, "the review's Back returns to the app step");
|
|
80
|
+
assert.match(HUB, /if \(step === 7\) syncAppStep\(\);\n\s*if \(step === 8\) renderReview\(\);/, 'entering the step repaints it from the draft');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('a fresh draft reads "decide later"', () => {
|
|
84
|
+
const w = load();
|
|
85
|
+
assert.equal(w.appPlatformChoice(), 'later');
|
|
86
|
+
assert.equal(w.appRow(), 'Not chosen yet — you can decide later');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('a draft that names Render while Render is not pickable still reads "decide later"', () => {
|
|
90
|
+
// The rule: the review never names a place the owner could not actually pick.
|
|
91
|
+
const w = load({ draft: { appPlatform: 'render' } });
|
|
92
|
+
assert.equal(w.appPlatformChoice(), 'later');
|
|
93
|
+
assert.equal(w.appRow(), 'Not chosen yet — you can decide later');
|
|
94
|
+
w.syncAppStep();
|
|
95
|
+
assert.deepEqual(w.inputs.filter((i) => i.checked).map((i) => i.value), ['later'], 'and the step shows Decide later checked');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('once Render is pickable, choosing it is what the review names', () => {
|
|
99
|
+
const w = load({ renderEnabled: true });
|
|
100
|
+
w.change('render');
|
|
101
|
+
assert.equal(w.state.appPlatform, 'render');
|
|
102
|
+
assert.deepEqual(w.saves, ['render'], 'the choice is saved to the draft');
|
|
103
|
+
assert.equal(w.appRow(), 'Render, on your own account');
|
|
104
|
+
w.syncAppStep();
|
|
105
|
+
assert.deepEqual(w.inputs.filter((i) => i.checked).map((i) => i.value), ['render']);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('a change on a disabled radio is ignored, and an unknown or missing value reads "decide later"', () => {
|
|
109
|
+
const w = load();
|
|
110
|
+
w.change('render');
|
|
111
|
+
assert.equal(w.state.appPlatform, 'later', 'a disabled option never reaches the draft');
|
|
112
|
+
assert.deepEqual(w.saves, []);
|
|
113
|
+
for (const v of [undefined, '', 'vercel', 'x"]']) {
|
|
114
|
+
assert.equal(load({ draft: { appPlatform: v } }).appPlatformChoice(), 'later', `appPlatform=${JSON.stringify(v)}`);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('the review carries the hall row and the app row in place of the old Shape row', () => {
|
|
119
|
+
const render = HUB.slice(HUB.indexOf('function renderReview()'));
|
|
120
|
+
const rows = render.slice(0, render.indexOf("$('reviewList').innerHTML"));
|
|
121
|
+
assert.ok(rows.includes("['Builders\\' hall', 'Hosted by us, on the platform’s current release'],"), 'the hall row');
|
|
122
|
+
assert.match(rows, /\['App', appRow\(\)\]/);
|
|
123
|
+
assert.doesNotMatch(rows, /\['Shape',/, 'the Shape row is gone');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('the rail grid has one column per step, so adding a step never wraps the rail', () => {
|
|
127
|
+
// Adding the app step made the rail eight beats in a seven-column grid: at 1440 the
|
|
128
|
+
// Create beat wrapped onto a second row. The count lives in the shared sheet, so the
|
|
129
|
+
// two must be checked against each other, not each on its own.
|
|
130
|
+
const css = fs.readFileSync(path.join(ROOT, 'modules', 'public-landing', 'public', 'assets', 'cosmos.css'), 'utf8');
|
|
131
|
+
const cols = Number((css.match(/#view-new \.steps\{display:grid;grid-template-columns:repeat\((\d+),1fr\)/) || [])[1]);
|
|
132
|
+
const beats = [...HUB.matchAll(/<li[^>]*data-step="\d+"/g)].length;
|
|
133
|
+
assert.equal(cols, beats, 'the #view-new rail grid carries exactly one column per rail step');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('the hall is spelled one way on this page', () => {
|
|
137
|
+
assert.doesNotMatch(HUB, /Builders Hall/, 'no "Builders Hall" beside the page\'s own "Builders\' hall"');
|
|
138
|
+
});
|
|
@@ -414,7 +414,7 @@ test('the review names the set even when the picker was never opened (task 10023
|
|
|
414
414
|
// and the module NAMES are not known. "None — just the core" would be a false
|
|
415
415
|
// promise on the last screen before Create: the server is about to apply the
|
|
416
416
|
// type's bundle. The row says that instead, then repaints when the read lands.
|
|
417
|
-
const h = boot({ state: { projType: 'game', step:
|
|
417
|
+
const h = boot({ state: { projType: 'game', step: 8 } }); // 8 = the review since task 1004351
|
|
418
418
|
|
|
419
419
|
// Arrange + Act — the review asks for the value before the read can answer
|
|
420
420
|
const first = h.extrasRow();
|
|
@@ -820,7 +820,7 @@ test('with nothing switched on the count says only what is shown', async () => {
|
|
|
820
820
|
});
|
|
821
821
|
|
|
822
822
|
test('the review names the hidden picks too — a filter never reaches the create request', async () => {
|
|
823
|
-
const h = boot({ state: { projType: 'game', step:
|
|
823
|
+
const h = boot({ state: { projType: 'game', step: 8 } }); // 8 = the review since task 1004351
|
|
824
824
|
await h.open();
|
|
825
825
|
h.click('agents');
|
|
826
826
|
assert.deepEqual(h.forCreate(), ['agents', 'dev-box', 'discord']);
|
|
@@ -241,11 +241,10 @@ test('no wall-clock promises, no operator vocabulary on the stranger’s path',
|
|
|
241
241
|
assert.doesNotMatch(HUB, /about five minutes/, 'the empty state');
|
|
242
242
|
assert.doesNotMatch(HUB, /a runner does the real work/, 'the mine fine print');
|
|
243
243
|
assert.doesNotMatch(SCRIPT, /pinned platform core/, 'the review Shape row');
|
|
244
|
-
/*
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
assert.match(SCRIPT, /\['Shape', 'Cloud host — we stand it up and run it for you, on the platform’s current release'\]/);
|
|
244
|
+
/* Since ADR 0345 (task 1004351) the review names the HALL as hosted by us and gives the
|
|
245
|
+
APP its own row. The assertion still guards the same property this test is about:
|
|
246
|
+
owner vocabulary, and no wall-clock promise. */
|
|
247
|
+
assert.ok(SCRIPT.includes("['Builders\\' hall', 'Hosted by us, on the platform’s current release'],"), 'the review names the hall as hosted by us');
|
|
249
248
|
assert.doesNotMatch(HUB, /\/new-project<\/code> assistant/, 'the done panel’s closing line');
|
|
250
249
|
assert.match(HUB, /Sign-in switches on after the GitHub steps|switch sign-in on/, 'the address step does not promise sign-in "just works"');
|
|
251
250
|
assert.doesNotMatch(HUB, /Sign-in, sharing, everything just works/);
|
package/tests/provision.mjs
CHANGED
|
@@ -130,25 +130,25 @@ t('planCorePinRefresh: a trailing file: pin → refresh; a current one → no-op
|
|
|
130
130
|
// The W3 shape: repo pins 1.19.2, control plane runs 1.19.196 → refresh (heals
|
|
131
131
|
// the silent-rollback hazard before the next repo-driven deploy).
|
|
132
132
|
assert.deepEqual(
|
|
133
|
-
P.planCorePinRefresh({ pinnedDep: 'file:vendor/bongos-core-1.19.2.tgz', coreVersion: '1.19.196' }),
|
|
133
|
+
P.planCorePinRefresh({ pinnedDep: 'file:vendor/bongos-core-1.19.2.tgz', version: '1.0.0', coreVersion: '1.19.196' }),
|
|
134
134
|
{ action: 'refresh', pinned: '1.19.2' });
|
|
135
135
|
// Pin already matches the running core → nothing to commit.
|
|
136
136
|
assert.deepEqual(
|
|
137
|
-
P.planCorePinRefresh({ pinnedDep: 'file:vendor/bongos-core-1.19.196.tgz', coreVersion: '1.19.196' }),
|
|
137
|
+
P.planCorePinRefresh({ pinnedDep: 'file:vendor/bongos-core-1.19.196.tgz', version: '1.0.0', coreVersion: '1.19.196' }),
|
|
138
138
|
{ action: 'current', pinned: '1.19.196' });
|
|
139
139
|
// A non-canonical file: pin can't be compared → refresh heals it to canonical.
|
|
140
140
|
assert.equal(
|
|
141
|
-
P.planCorePinRefresh({ pinnedDep: 'file:vendor/core.tgz', coreVersion: '1.19.196' }).action,
|
|
141
|
+
P.planCorePinRefresh({ pinnedDep: 'file:vendor/core.tgz', version: '1.0.0', coreVersion: '1.19.196' }).action,
|
|
142
142
|
'refresh');
|
|
143
143
|
});
|
|
144
144
|
|
|
145
145
|
t('planCorePinRefresh: registry pins and absent deps are skipped, never rewritten', () => {
|
|
146
146
|
// Registry pin (ADR 0134): npm resolves it within its range — not ours to vendor.
|
|
147
|
-
assert.equal(P.planCorePinRefresh({ pinnedDep: '^1.19.100', coreVersion: '1.19.196' }).action, 'skip');
|
|
148
|
-
assert.equal(P.planCorePinRefresh({ pinnedDep: '1.19.196', coreVersion: '1.19.196' }).action, 'skip');
|
|
147
|
+
assert.equal(P.planCorePinRefresh({ pinnedDep: '^1.19.100', version: '1.0.0', coreVersion: '1.19.196' }).action, 'skip');
|
|
148
|
+
assert.equal(P.planCorePinRefresh({ pinnedDep: '1.19.196', version: '1.0.0', coreVersion: '1.19.196' }).action, 'skip');
|
|
149
149
|
// No @bongos/core dependency at all → not ours to invent.
|
|
150
|
-
assert.equal(P.planCorePinRefresh({ pinnedDep: undefined, coreVersion: '1.19.196' }).action, 'skip');
|
|
151
|
-
assert.equal(P.planCorePinRefresh({ pinnedDep: '', coreVersion: '1.19.196' }).action, 'skip');
|
|
150
|
+
assert.equal(P.planCorePinRefresh({ pinnedDep: undefined, version: '1.0.0', coreVersion: '1.19.196' }).action, 'skip');
|
|
151
|
+
assert.equal(P.planCorePinRefresh({ pinnedDep: '', version: '1.0.0', coreVersion: '1.19.196' }).action, 'skip');
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
t('safeVersionLabel: passes clean semver, rejects any shell metacharacter', () => {
|
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
OPTIONAL_MODULES,
|
|
38
38
|
STARTER_BUNDLES,
|
|
39
39
|
HUB_ONLY_MODULES,
|
|
40
|
-
RETIRED_MODULES,
|
|
41
40
|
RULED_TEAM_SHAPES,
|
|
42
41
|
bundleForProjectType,
|
|
43
42
|
allStarterBundles,
|
|
@@ -336,9 +335,6 @@ test('the opt-in catalog declared for the picker IS the on-disk opt-in set', ()
|
|
|
336
335
|
const onDisk = manifestsOnDisk();
|
|
337
336
|
const optInOnDisk = [...onDisk.values()]
|
|
338
337
|
.filter((m) => m.default !== true && !HUB_ONLY_MODULES.includes(m.key))
|
|
339
|
-
// A RETIRED module is still on disk while its removal lands, and must already
|
|
340
|
-
// be gone from the catalog — so it is the one permitted absence (task 1003886).
|
|
341
|
-
.filter((m) => !RETIRED_MODULES.includes(m.key))
|
|
342
338
|
.map((m) => m.key)
|
|
343
339
|
.sort();
|
|
344
340
|
|
|
@@ -350,24 +346,6 @@ test('the opt-in catalog declared for the picker IS the on-disk opt-in set', ()
|
|
|
350
346
|
'the picker\'s catalog has drifted from the manifests — a module was added, removed, or flipped its default');
|
|
351
347
|
});
|
|
352
348
|
|
|
353
|
-
test('a RETIRED module is withdrawn everywhere an owner could still reach it', () => {
|
|
354
|
-
// The retirement is only real if it holds at every offer point at once. A key
|
|
355
|
-
// left in any one of them is still a module a new project can end up with,
|
|
356
|
-
// which is exactly what the withdrawal exists to stop (task 1003886).
|
|
357
|
-
for (const key of RETIRED_MODULES) {
|
|
358
|
-
assert.ok(!OPTIONAL_MODULES.some((m) => m.key === key),
|
|
359
|
-
`retired module "${key}" is still a row the creation picker offers`);
|
|
360
|
-
for (const [type, bundle] of Object.entries(STARTER_BUNDLES)) {
|
|
361
|
-
assert.ok(!bundle.modules.includes(key),
|
|
362
|
-
`retired module "${key}" is still preselected by the "${type}" bundle`);
|
|
363
|
-
}
|
|
364
|
-
// Posting it directly is a real answer that resolves to nothing, not a
|
|
365
|
-
// non-answer: the key is DROPPED, exactly as an unknown one would be.
|
|
366
|
-
assert.deepEqual(normalizeModuleSelection([key]), [],
|
|
367
|
-
`retired module "${key}" is still accepted when an owner posts it directly`);
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
|
|
371
349
|
test('every opt-in module carries owner-facing copy, and no module jargon rides along', () => {
|
|
372
350
|
for (const row of OPTIONAL_MODULES) {
|
|
373
351
|
assert.ok(row.label && row.label.length <= 40, `"${row.key}" needs a short label`);
|
|
@@ -9,12 +9,10 @@
|
|
|
9
9
|
// 1. src/bongos/github-push.js — the push/PR/status logic, exercised through the
|
|
10
10
|
// injectable exec + fetch seams (deps.exec / deps.fetchImpl / deps.token /
|
|
11
11
|
// deps.repoInfo / deps.mkdtemp / deps.rmtmp).
|
|
12
|
-
// 2. The route wiring — a STRUCTURAL guard that POST/GET publish*
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// out of it — a future edit that added allowBoxScope would reintroduce the
|
|
17
|
-
// "a leaked box token can ship arbitrary code" hole, and this test fails first.
|
|
12
|
+
// 2. The route wiring — a STRUCTURAL guard that POST/GET publish* and the
|
|
13
|
+
// attest route are requireBuilder-gated, so an unauthenticated caller (or a
|
|
14
|
+
// session of an undeclared source — tests/government_session_scope.mjs) can
|
|
15
|
+
// never publish.
|
|
18
16
|
//
|
|
19
17
|
// Run: node tests/publish_branch_route.mjs
|
|
20
18
|
|
|
@@ -407,8 +405,7 @@ await t('getPublishStatus: merged, workflow EXISTS but no successful run yet →
|
|
|
407
405
|
});
|
|
408
406
|
|
|
409
407
|
// ===========================================================================
|
|
410
|
-
// Layer 2 — STRUCTURAL guard: the publish routes are requireBuilder
|
|
411
|
-
// NO allowBoxScope (so a box-scoped session is denied — #919 regression guard).
|
|
408
|
+
// Layer 2 — STRUCTURAL guard: the publish routes are requireBuilder-gated.
|
|
412
409
|
// ===========================================================================
|
|
413
410
|
function findRouteLayer(router, routePath, method) {
|
|
414
411
|
return router.stack.find(
|
|
@@ -419,36 +416,31 @@ function handlesOf(layer) {
|
|
|
419
416
|
return layer.route.stack.map((s) => s.handle);
|
|
420
417
|
}
|
|
421
418
|
|
|
422
|
-
await t('POST /tasks/:id/publish-branch: requireBuilder present
|
|
419
|
+
await t('POST /tasks/:id/publish-branch: requireBuilder present', async () => {
|
|
423
420
|
const router = buildTasksRouter();
|
|
424
421
|
const layer = findRouteLayer(router, '/tasks/:id/publish-branch', 'post');
|
|
425
422
|
assert.ok(layer, 'the publish-branch POST route must be registered');
|
|
426
423
|
const handles = handlesOf(layer);
|
|
427
424
|
assert.ok(handles.includes(auth.requireBuilder), 'must be requireBuilder-gated');
|
|
428
|
-
assert.ok(!handles.includes(auth.allowBoxScope), 'must NOT carry allowBoxScope — a box session must be denied (#919)');
|
|
429
425
|
});
|
|
430
426
|
|
|
431
|
-
await t('GET /tasks/:id/publish-status: requireBuilder present
|
|
427
|
+
await t('GET /tasks/:id/publish-status: requireBuilder present', async () => {
|
|
432
428
|
const router = buildTasksRouter();
|
|
433
429
|
const layer = findRouteLayer(router, '/tasks/:id/publish-status', 'get');
|
|
434
430
|
assert.ok(layer, 'the publish-status GET route must be registered');
|
|
435
431
|
const handles = handlesOf(layer);
|
|
436
432
|
assert.ok(handles.includes(auth.requireBuilder), 'must be requireBuilder-gated');
|
|
437
|
-
assert.ok(!handles.includes(auth.allowBoxScope), 'must NOT carry allowBoxScope');
|
|
438
433
|
});
|
|
439
434
|
|
|
440
435
|
// task 1169 (ADR 0058): the laptop ci-land attest endpoint. It posts the trustless
|
|
441
|
-
// gate-author-trust commit status for a Metic+ author, so
|
|
442
|
-
//
|
|
443
|
-
|
|
444
|
-
// box gate (no allowBoxScope), matching publish-branch / publish-status exactly.
|
|
445
|
-
await t('POST /tasks/:id/attest-gate: requireBuilder present, allowBoxScope ABSENT', async () => {
|
|
436
|
+
// gate-author-trust commit status for a Metic+ author, so it carries the same
|
|
437
|
+
// requireBuilder gate as publish-branch / publish-status.
|
|
438
|
+
await t('POST /tasks/:id/attest-gate: requireBuilder present', async () => {
|
|
446
439
|
const router = buildTasksRouter();
|
|
447
440
|
const layer = findRouteLayer(router, '/tasks/:id/attest-gate', 'post');
|
|
448
441
|
assert.ok(layer, 'the attest-gate POST route must be registered');
|
|
449
442
|
const handles = handlesOf(layer);
|
|
450
443
|
assert.ok(handles.includes(auth.requireBuilder), 'must be requireBuilder-gated');
|
|
451
|
-
assert.ok(!handles.includes(auth.allowBoxScope), 'must NOT carry allowBoxScope — a box session must reauth, never self-attest (#919)');
|
|
452
444
|
});
|
|
453
445
|
|
|
454
446
|
// task 1002645: the attest call is the ONE moment the laptop ci-land tells the
|
|
@@ -57,8 +57,7 @@ const PUBLISH = [
|
|
|
57
57
|
'modules/lifecycle/db.js',
|
|
58
58
|
'modules/grading/grader.js',
|
|
59
59
|
'modules/hall-ui/public/work.js',
|
|
60
|
-
'modules/
|
|
61
|
-
'modules/provisioning/provisioning.js', // instance-hosting control plane — CORE like its sibling dev-box (task 2135)
|
|
60
|
+
'modules/provisioning/provisioning.js', // instance-hosting control plane — CORE (task 2135)
|
|
62
61
|
// served-at-runtime surfaces a standalone must carry (task 2078) — their absence
|
|
63
62
|
// from the packaged core 404'd the hall UI + /docs on demo.cloudbongos.com
|
|
64
63
|
'clients/bongos-client/bongos-client.global.js', // /lib/bongos-client.js (window.BongosClient)
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
// modules/discord/discord-channels.js): dependency-free substrate
|
|
13
13
|
// (permission-path-check runs in a bare git hook), source-parsed literals
|
|
14
14
|
// (src/bongos/db.js — gate-review.js + diagram-facts.js regex its text),
|
|
15
|
-
// standalone CLIs (sequence),
|
|
16
|
-
// module template (boxes.js), zero-dep catalog + its text-parsing auditor
|
|
15
|
+
// standalone CLIs (sequence), zero-dep catalog + its text-parsing auditor
|
|
17
16
|
// (route-rank-check), and the hall browser statics.
|
|
18
17
|
// 3. NO NEW parallel map can land: a repo scan flags any rank→number map or
|
|
19
18
|
// rank-ladder array literal outside the allowlist below, both directions —
|
|
@@ -38,7 +37,6 @@ const lifecycleDb = require(path.join(ROOT, 'modules', 'lifecycle', 'db.js'));
|
|
|
38
37
|
const ppc = require(path.join(ROOT, 'src', 'bongos', 'permission-path-check.js'));
|
|
39
38
|
const rrc = require(path.join(ROOT, 'src', 'bongos', 'route-rank-check.js'));
|
|
40
39
|
const sequence = require(path.join(ROOT, 'scripts', 'gds', 'sequence.js'));
|
|
41
|
-
const boxes = require(path.join(ROOT, 'modules', 'dev-box', 'boxes.js'));
|
|
42
40
|
const catalog = require(path.join(ROOT, 'modules', 'government', 'catalog.js'));
|
|
43
41
|
|
|
44
42
|
const LADDER = kernel.LIVE_RANK_LADDER;
|
|
@@ -81,15 +79,6 @@ test('1-based local copies match: permission-path-check, sequence CLI', () => {
|
|
|
81
79
|
assert.deepEqual(sequence.RANK_TIER, TIER, 'sequence.js CLI copy drifted');
|
|
82
80
|
});
|
|
83
81
|
|
|
84
|
-
test('0-based local copies match: dev-box boxes.js', () => {
|
|
85
|
-
for (const [name, map] of [['boxes', boxes.RANK_ORDER]]) {
|
|
86
|
-
assert.deepEqual(Object.keys(map).sort(), [...RANK_KEYS].sort(), `${name} keys drifted`);
|
|
87
|
-
for (const r of RANK_KEYS) {
|
|
88
|
-
assert.equal(map[r], TIER[r] - 1, `${name}.RANK_ORDER.${r} must be tier-1 (0-based)`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
82
|
test('ladder mirrors match: governance catalog, route-rank-check', () => {
|
|
94
83
|
assert.deepEqual([...catalog.RANK_ORDER], [...LADDER]);
|
|
95
84
|
assert.deepEqual([...rrc.PERMISSION_RANK_ORDER], [...LADDER]);
|
|
@@ -192,7 +181,6 @@ const MAP_ALLOWLIST = new Set([
|
|
|
192
181
|
'src/bongos/db.js',
|
|
193
182
|
'src/bongos/permission-path-check.js',
|
|
194
183
|
'scripts/gds/sequence.js',
|
|
195
|
-
'modules/dev-box/boxes.js',
|
|
196
184
|
'modules/hall-ui/public/builders.js',
|
|
197
185
|
// task 1003206 carved the hall page scripts up. builders.js KEPT its map (the
|
|
198
186
|
// renderers read it through the injected ctx), while goals-page.js's travelled
|
package/tests/repo_map.mjs
CHANGED
|
@@ -87,10 +87,9 @@ test('extractor parses functions, classes, arrow consts, and module.exports', ()
|
|
|
87
87
|
// exercise the pure extractor against the real auth.js + a known class file
|
|
88
88
|
const r = run(['--json']);
|
|
89
89
|
const model = JSON.parse(r.stdout);
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
assert.ok(cfTunnel && cfTunnel.kind === 'class', 'class declarations are detected');
|
|
90
|
+
// DoApi is a plain `class X extends Y` declaration in a control-plane client.
|
|
91
|
+
const doApi = model.allSyms.find((s) => s.name === 'DoApi' && s.file === 'scripts/gds/do-api.js');
|
|
92
|
+
assert.ok(doApi && doApi.kind === 'class', 'class declarations are detected');
|
|
94
93
|
const apiCall = model.allSyms.find((s) => s.name === 'apiCall' && s.file === 'scripts/gds/cli-lib.js');
|
|
95
94
|
assert.ok(apiCall && apiCall.exported && apiCall.kind === 'fn', 'exported arrow/fn const detected with export flag');
|
|
96
95
|
});
|
package/tests/runner_drift.mjs
CHANGED
|
@@ -77,9 +77,9 @@ t('assessRunnerUnit: a drop-in that ADDS an ExecStart without clearing leaves th
|
|
|
77
77
|
t('runnerScriptPath: node + a relative script resolves against WorkingDirectory; a wrapper resolves to null', () => {
|
|
78
78
|
assert.equal(runnerScriptPath({ workingDirectory: INSTANCE, execStart: '/usr/bin/node node_modules/@bongos/core/scripts/gds/provision.js run-intents --apply' }),
|
|
79
79
|
`${INSTANCE}/node_modules/@bongos/core/scripts/gds/provision.js`);
|
|
80
|
-
assert.equal(runnerScriptPath({ workingDirectory: '/w', execStart: '/usr/bin/env node /abs/core/scripts/gds/
|
|
81
|
-
assert.equal(runnerScriptPath({ workingDirectory: '/w', execStart: '-/usr/bin/node scripts/gds/
|
|
82
|
-
assert.equal(runnerScriptPath({ workingDirectory: '/w', execStart: '/usr/bin/env NODE_ENV=production node --max-old-space-size=512 scripts/gds/
|
|
80
|
+
assert.equal(runnerScriptPath({ workingDirectory: '/w', execStart: '/usr/bin/env node /abs/core/scripts/gds/provision.js run-intents' }), '/abs/core/scripts/gds/provision.js');
|
|
81
|
+
assert.equal(runnerScriptPath({ workingDirectory: '/w', execStart: '-/usr/bin/node scripts/gds/provision.js' }), '/w/scripts/gds/provision.js', 'systemd exec prefixes are stripped');
|
|
82
|
+
assert.equal(runnerScriptPath({ workingDirectory: '/w', execStart: '/usr/bin/env NODE_ENV=production node --max-old-space-size=512 scripts/gds/provision.js' }), '/w/scripts/gds/provision.js', 'env assignments and node flags are skipped');
|
|
83
83
|
assert.equal(runnerScriptPath({ workingDirectory: '/w', execStart: '/usr/bin/env bash infra/core-node.sh scripts/gds/provision.js' }), null,
|
|
84
84
|
'a shell wrapper decides at runtime which core it loads — the gate must not guess');
|
|
85
85
|
});
|
|
@@ -130,24 +130,6 @@ t('runnerBanner: names the runner core + path, and flags a differing instance co
|
|
|
130
130
|
assert.equal(runnerBanner({ runnerVersion: null, runnerRoot: '/r', instanceVersion: null, instanceRoot: '/i' }), 'runner core ? from /r (no installed core at /i)');
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
-
// box.js run-intents is the other runner the incident class covers — it opens with the same line.
|
|
134
|
-
const { cmdRunIntents: boxRunIntents } = await import('../scripts/gds/box.js');
|
|
135
|
-
{
|
|
136
|
-
const lines = [];
|
|
137
|
-
const origLog = console.log;
|
|
138
|
-
const origRoot = process.env.BONGOS_INSTANCE_ROOT;
|
|
139
|
-
process.env.BONGOS_INSTANCE_ROOT = '/no/such/instance';
|
|
140
|
-
console.log = (l) => lines.push(String(l));
|
|
141
|
-
try { await boxRunIntents({ query: async () => ({ rows: [] }) }, {}, null, { apply: false }); }
|
|
142
|
-
finally {
|
|
143
|
-
console.log = origLog;
|
|
144
|
-
if (origRoot === undefined) delete process.env.BONGOS_INSTANCE_ROOT; else process.env.BONGOS_INSTANCE_ROOT = origRoot;
|
|
145
|
-
}
|
|
146
|
-
t('box.js run-intents opens with the runner core version line', () => {
|
|
147
|
-
assert.match(lines[0], /^runner core \d+\.\d+\.\d+ from .+ \(no installed core at \/no\/such\/instance\)$/);
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
133
|
const { runnerDriftRows } = await import('../scripts/gds/doctor.js');
|
|
152
134
|
|
|
153
135
|
t('doctor maps each verdict to a row: ok → ok, drift → FAIL with the fix, unresolved → warn', () => {
|