@bongos/core 1.19.1081 → 1.20.2
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 +528 -193
- package/.claude/skills/planning-session/SKILL.md +6 -2
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +32 -0
- package/clients/bongos-client/index.cjs +32 -0
- package/clients/bongos-client/index.d.ts +51 -6
- package/clients/bongos-client/index.mjs +32 -0
- package/docs/adr/0099-delayed-redacted-mirror-export.md +5 -1
- package/docs/adr/0111-instance-hosting-provisioning-module.md +1 -0
- package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +17 -0
- package/docs/adr/0176-private-repo-deploy-keys.md +2 -0
- package/docs/adr/0310-a-speciality-offers-skills-and-the-adopter-chooses-them.md +1 -1
- package/docs/adr/0348-the-web-tier-may-look-read-only-at-what-an-owners-render-key-can-see.md +53 -0
- package/docs/adr/0349-a-version-preview-is-a-sandboxed-child-the-web-tier-launches.md +106 -0
- package/docs/adr/0350-the-hub-holds-a-per-project-write-deploy-key-so-a-hosted-upgrade-reaches-github.md +103 -0
- package/docs/adr/README.md +3 -0
- package/docs/api/openapi.json +851 -19
- package/docs/api-reference.md +33 -9
- package/docs/architecture.md +6 -2
- package/docs/copy-inventory.md +620 -553
- package/docs/copy-registry.json +1467 -820
- package/docs/file-map.md +2 -0
- package/docs/module-api-changelog.md +8 -0
- package/docs/modules-contract.md +1 -0
- package/docs/page-inventory.json +38 -4
- package/docs/page-readings.json +1666 -1558
- package/migrations/core_260_goals_working_area.sql +63 -0
- package/migrations/core_261_grade_attempts_verdicts.sql +24 -0
- package/migrations/core_262_drop_builder_box_blocked.sql +33 -0
- package/modules/agents/lib/validate.js +43 -0
- package/modules/autonomy/gauge.js +38 -2
- package/modules/grading/grader-subagent.js +37 -2
- package/modules/grading/grader-workers/reader.js +65 -5
- package/modules/hall-ui/public/approval-queue.css +33 -7
- package/modules/hall-ui/public/approval-queue.js +9 -2
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/brand-holes.js +121 -0
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/collab.js +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/deploy.html +6 -1
- package/modules/hall-ui/public/deploy.js +22 -8
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/dom-utils.js +20 -0
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/fleet.html +1 -1
- package/modules/hall-ui/public/gate.html +1 -1
- package/modules/hall-ui/public/goal-inbox.js +1 -1
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/idea.js +28 -0
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/ideas.js +19 -3
- package/modules/hall-ui/public/index.html +5 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile-nudge.js +21 -4
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/profile.js +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +19 -30
- package/modules/hall-ui/public/settings.js +24 -177
- package/modules/hall-ui/public/settings.states.json +1 -1
- package/modules/hall-ui/public/shell.js +4 -2
- package/modules/hall-ui/public/studio.css +14 -8
- package/modules/hall-ui/public/studio.html +5 -4
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.css +6 -3
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/tweak-editor-lib.js +82 -1
- package/modules/hall-ui/public/tweak-editor.css +142 -11
- package/modules/hall-ui/public/tweak-editor.html +25 -9
- package/modules/hall-ui/public/tweak-editor.js +243 -10
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/hall-ui/records/approval-queue.md +6 -0
- package/modules/hall-ui/records/tweak-editor.md +6 -0
- package/modules/ideas/ratify.js +36 -8
- package/modules/ideas/routes/ratify-goal.js +6 -0
- package/modules/lifecycle/db-analytics.js +79 -10
- package/modules/lifecycle/db-claims.js +48 -1
- package/modules/lifecycle/db-deps-criteria.js +94 -40
- package/modules/lifecycle/db-goals.js +4 -1
- package/modules/lifecycle/db-grade.js +9 -1
- package/modules/lifecycle/db-relevance-flags.js +183 -0
- package/modules/lifecycle/db-versions.js +4 -3
- package/modules/lifecycle/db.js +19 -0
- package/modules/lifecycle/done-when.js +5 -3
- package/modules/lifecycle/est-advisory.js +31 -4
- package/modules/lifecycle/goal-task-relevance-advisory.js +153 -0
- package/modules/lifecycle/goal-task-relevance-judge.js +226 -0
- package/modules/lifecycle/migrations/lifecycle_014_goal_task_relevance_flags.sql +82 -0
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/routes/claims.js +8 -1
- package/modules/lifecycle/routes/goal-task-relevance.js +72 -0
- package/modules/lifecycle/routes/goals.js +1 -1
- package/modules/lifecycle/routes/tasks.js +27 -24
- package/modules/lifecycle/routes/visuals.js +9 -0
- package/modules/lifecycle/ship-preflight.js +7 -11
- package/modules/lifecycle/task-visuals.js +109 -5
- package/modules/npm-release/module.json +2 -1
- package/modules/npm-release/preview/commands.js +81 -0
- package/modules/npm-release/preview/divert.js +95 -0
- package/modules/npm-release/preview/env.js +78 -0
- package/modules/npm-release/preview/proxy.js +107 -0
- package/modules/npm-release/preview/runtime.js +83 -0
- package/modules/npm-release/preview/supervisor.js +175 -0
- package/modules/npm-release/public/work.js +65 -1
- package/modules/npm-release/routes/preview.js +133 -0
- package/modules/provisioning/app-status.js +83 -0
- package/modules/provisioning/module.json +2 -1
- package/modules/provisioning/pollers/app-liveness.js +110 -0
- package/modules/provisioning/render-lookup.js +68 -0
- package/modules/provisioning/render-standup.js +44 -1
- package/modules/provisioning/routes/render-standup.js +81 -6
- package/modules/provisioning/starter-bundles.js +4 -1
- package/modules/public-landing/public/account-private.states.json +14 -0
- package/modules/public-landing/public/account.html +513 -0
- package/modules/public-landing/public/account.probes.json +37 -0
- package/modules/public-landing/public/account.states.json +16 -0
- package/modules/public-landing/public/index.html +5 -2
- package/modules/public-landing/public/projects.html +661 -18
- package/modules/public-landing/public/projects.states.json +8 -1
- package/modules/render-deploy/deploys.js +203 -0
- package/modules/render-deploy/migrations/render_deploy_001_app.sql +22 -0
- package/modules/render-deploy/module.json +25 -0
- package/modules/render-deploy/public/deploy.css +10 -0
- package/modules/render-deploy/public/deploy.js +249 -0
- package/modules/render-deploy/render.js +56 -0
- package/modules/render-deploy/routes/act.js +88 -0
- package/modules/render-deploy/routes/app.js +65 -0
- package/modules/render-deploy/routes/history.js +38 -0
- package/modules/specialities/db.js +27 -8
- package/modules/specialities/migrations/specialities_003_skills.sql +46 -0
- package/modules/specialities/routes/specialities.js +51 -3
- package/modules/specialities/skills.js +94 -0
- package/modules/specialities/specialities.js +56 -9
- package/modules/ui-design/kit/fixtures/me-cross-project-private.json +12 -0
- package/modules/ui-design/kit/fixtures/me__cross-project.json +15 -0
- package/modules/ui-design/kit/fixtures/provisioning-instance-render.json +39 -0
- package/modules/ui-design/kit/lib.js +3 -1
- package/modules/ui-design/kit/serve.js +96 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +169 -0
- package/scripts/gds/agents-sync.js +13 -3
- package/scripts/gds/autobongos-run.js +80 -2
- package/scripts/gds/autobongos-service.cmd +12 -0
- package/scripts/gds/copy-apply.js +14 -0
- package/scripts/gds/dev-box-guard.js +3 -2
- package/scripts/gds/fitness.js +9 -0
- package/scripts/gds/grade-correlation-audit.js +42 -12
- package/scripts/gds/grade-replay.js +1 -1
- package/scripts/gds/provision-core-upgrade.js +38 -7
- package/scripts/gds/provision-pin-key.js +176 -0
- package/scripts/gds/provision-render.js +62 -31
- package/scripts/gds/provision-teardown.js +7 -2
- package/scripts/gds/provision-units.js +31 -0
- package/scripts/gds/provision.js +10 -10
- package/scripts/gds/publish-manifest.js +1 -0
- package/scripts/gds/render-api.js +19 -0
- package/scripts/gds/render-payload.js +39 -65
- package/scripts/gds/run-unit-tests.js +10 -0
- package/scripts/gds/ship-finish.js +19 -20
- package/scripts/gds/smoke-dependencies.sh +37 -8
- package/scripts/gds/status.js +12 -4
- package/scripts/gds/upgrade.js +2 -2
- package/scripts/public-mirror-export.js +7 -1
- package/src/bongos/module-scope-map.js +13 -0
- package/src/bongos/serve-internal.js +3 -1
- package/src/module-api.js +10 -1
- package/src/platform-server.js +39 -0
- package/tests/account_privacy_flags.mjs +10 -6
- package/tests/agents_authoring.mjs +15 -1
- package/tests/agents_sync.mjs +106 -5
- package/tests/agents_validate.mjs +45 -0
- package/tests/api_path_404.mjs +6 -0
- package/tests/autobongos_cadence.mjs +7 -0
- package/tests/autobongos_loop.mjs +136 -1
- package/tests/autonomy_gauge.mjs +62 -0
- package/tests/blocker_hall_live_proof.mjs +275 -0
- package/tests/claim_gate_ci_unblock.mjs +116 -0
- package/tests/claim_gate_rebase.mjs +15 -3
- package/tests/collab_page.mjs +10 -0
- package/tests/conductor_main_e2e.mjs +97 -0
- package/tests/core_262_drop_box_blocked_db.mjs +143 -0
- package/tests/core_upgrade_runner.mjs +1 -0
- package/tests/dependency_writes_atomic.mjs +191 -0
- package/tests/deploy_page_projects.mjs +2 -1
- package/tests/effective_visibility_predicate.mjs +15 -0
- package/tests/est_advisory.mjs +27 -0
- package/tests/goal_map_page.mjs +26 -0
- package/tests/goal_task_relevance.mjs +518 -0
- package/tests/goal_working_area.mjs +146 -0
- package/tests/grade_attempts.mjs +61 -0
- package/tests/grade_attribution.mjs +23 -0
- package/tests/grade_correlation_audit.mjs +54 -1
- package/tests/grader_reader_lens.mjs +55 -2
- package/tests/grader_root_outage.mjs +6 -1
- package/tests/grader_subagent_tools_arg.mjs +102 -0
- package/tests/hall_approval_queue.mjs +54 -2
- package/tests/hall_settings_world.mjs +3 -1
- package/tests/hall_tweak_editor.mjs +316 -11
- package/tests/hub_account_page.mjs +394 -0
- package/tests/idea_detail_page.mjs +64 -0
- package/tests/idea_goal_ratification.mjs +64 -0
- package/tests/idea_spark_hall.mjs +60 -9
- package/tests/ideator_full_idea_shapes_space_proof.mjs +1 -1
- package/tests/landing_page.mjs +6 -3
- package/tests/module-scope-map.mjs +31 -8
- package/tests/module_api.mjs +1 -0
- package/tests/module_loader.mjs +1 -1
- package/tests/nav_permission_atoms.mjs +1 -1
- package/tests/no_phantom_mirror_workflow.mjs +24 -0
- package/tests/npm_release_preview_commands.mjs +89 -0
- package/tests/npm_release_preview_divert.mjs +164 -0
- package/tests/npm_release_preview_env.mjs +100 -0
- package/tests/npm_release_preview_proxy.mjs +162 -0
- package/tests/npm_release_preview_routes.mjs +208 -0
- package/tests/npm_release_preview_supervisor.mjs +203 -0
- package/tests/pin_write_key.mjs +383 -0
- package/tests/planning_session_skill.mjs +27 -0
- package/tests/platform_boot.mjs +71 -0
- package/tests/profile_nudge_links.mjs +86 -0
- package/tests/profile_ui_cross_project.mjs +1 -1
- package/tests/projects_hub.mjs +2 -2
- package/tests/projects_hub_app_status.mjs +249 -0
- package/tests/projects_hub_app_step.mjs +282 -52
- package/tests/projects_hub_render_connect.mjs +295 -0
- package/tests/provision_render.mjs +121 -28
- package/tests/provision_settings_apply.mjs +62 -0
- package/tests/provisioning_app_status.mjs +204 -0
- package/tests/provisioning_render_route.mjs +126 -6
- package/tests/render_check.mjs +2 -1
- package/tests/render_deploy.mjs +495 -0
- package/tests/rot_inbox.mjs +8 -0
- package/tests/ship_preflight.mjs +61 -8
- package/tests/smoke_dependencies_witness.mjs +46 -0
- package/tests/speciality_skills.mjs +214 -0
- package/tests/studio_room_height.mjs +70 -0
- package/tests/task_detail_includes.mjs +10 -0
- package/tests/task_visuals_instance_root.mjs +150 -0
- package/tests/tweak_batch_apply.mjs +21 -0
- package/tests/ui_design_kit.mjs +36 -4
- package/tests/upgrade.mjs +1 -1
- package/tests/upgrade_persist_pin.mjs +24 -0
package/scripts/gds/provision.js
CHANGED
|
@@ -50,7 +50,7 @@ const crypto = require('node:crypto');
|
|
|
50
50
|
|
|
51
51
|
const { CONFIG, MANIFEST_UA, softFailResult, MANIFEST_VERIFY_INTERVAL_MS, MANIFEST_VERIFY_TRIES, MAX_INTENT_ATTEMPTS, REPO_ROOT, coreVersionSafe, hasFlag, loadDeps, oauthSecret, provisionerBotEmail } = require('./provision-config.js');
|
|
52
52
|
const { accountPreflight, appGroupExpr, appUser, alreadyScaffolded, buildCorePinRefreshCommit, coreCheckoutHasGit, dbCreateCmd, dbName, dbRoleCmd, dbRoleCmdShown, dbRoleGrantsCmd, ensureInstanceUserCmd, generateDbPassword, grantInstanceRepoReadCmd, instanceDbRole, instanceStateDir, instanceUser, deployKeyPath, deployKeyTitle, ensurePrivateRepoAccess, installedCorePackDir, installedCoreTarball, instanceInitSpec, instanceRepoRemote, migrateCmd, onboardMode, ownerLoginOf, parseTargetRef, planCorePinRefresh, refreshStandaloneCorePin, resolveOwnerGithubToken, resolveVendorableCoreTarball, safeVersionLabel, scaffoldStandaloneRepo, seedFirstVersionCmd, standaloneInstallCmd, standaloneMigrateCmd, standalonePullCmd, standaloneRegenDocsCmd, standaloneRoot } = require('./provision-repo.js');
|
|
53
|
-
const { backupScriptPath, backupService, backupServicePath, backupTimer, backupTimerPath, backupUnitName, instanceManifestCmd, originEnvVarsFor, serviceUnit, serviceUnitPath, settingsConsumed, settingsEnvVarsFor, upsertEnvVars, webEnvBody, webEnvPath } = require('./provision-units.js');
|
|
53
|
+
const { backupScriptPath, backupService, backupServicePath, backupTimer, backupTimerPath, backupUnitName, instanceManifestCmd, originEnvVarsFor, serviceUnit, serviceUnitPath, settingsConsumed, settingsEnvVarsFor, renamedEnvCarry, upsertEnvVars, webEnvBody, webEnvPath } = require('./provision-units.js');
|
|
54
54
|
const { caddyBlock, caddySnippetPath, checkCaddyWiring, checkDnsTokenScope, classifyZoneScope, dnsReleaseEnv, dnsUpsertEnv, federateInstance, federationHubOrigin, healthzCmd, identityCmd, identityVerdict, ingressPlaneGap, ingressPreflight, shouldFederate, zoneForDomain } = require('./provision-net.js');
|
|
55
55
|
const { resolveProcessManager } = require('./provision-process.js');
|
|
56
56
|
const { capacityPreflight, statusCapacityLine } = require('./provision-capacity.js');
|
|
@@ -374,10 +374,10 @@ async function provisionInstance(inst, deps) {
|
|
|
374
374
|
// throw: the greenfield flow now has no manual clone step. Idempotent + dry-run-safe.
|
|
375
375
|
if (inst.hosting_shape === 'cloud-host') {
|
|
376
376
|
await scaffoldStandaloneRepo(inst, deps);
|
|
377
|
-
// A PRIVATE target_ref gets its own read-only deploy key BEFORE step 2's
|
|
378
|
-
//
|
|
379
|
-
// on every private standup (walk finding N4; ADR 0176).
|
|
377
|
+
// A PRIVATE target_ref gets its own read-only deploy key BEFORE step 2's `git pull
|
|
378
|
+
// --ff-only origin main`, which failed unauthenticated on every private standup (N4; ADR 0176).
|
|
380
379
|
await ensurePrivateRepoAccess(inst, deps);
|
|
380
|
+
await require('./provision-pin-key.js').ensurePinWriteKey(inst, deps); // the hub's WRITE key for later pin pushes, public repos too (task 1004291, ADR 0350)
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
383
|
if (!inst.db_name) { patch.db_name = inst.slug; inst = { ...inst, db_name: inst.slug }; }
|
|
@@ -904,7 +904,7 @@ async function settingsApplyInstance(inst, deps) {
|
|
|
904
904
|
// exists to avoid). An unreadable or empty file is refused, never "patched":
|
|
905
905
|
// writing two lines over a missing PORT/PGDATABASE would take the instance down.
|
|
906
906
|
const read = boxExec(`${sudoP}cat ${webEnvPath(inst)}`, { allowFail: true });
|
|
907
|
-
const current = String((read && read.stdout) || '');
|
|
907
|
+
const current = String((read && read.stdout) || ''), carry = renamedEnvCarry(current); // task 1004395: only its NAMES are ever logged
|
|
908
908
|
if (!apply) {
|
|
909
909
|
log(` [config] would patch ${Object.keys(vars).join(', ')} in ${webEnvPath(inst)} in place (every other line untouched)`);
|
|
910
910
|
pmFor(deps).restartService(inst, { exec: boxExec, sudoP });
|
|
@@ -916,8 +916,8 @@ async function settingsApplyInstance(inst, deps) {
|
|
|
916
916
|
return { ok: false, error: `cannot read ${webEnvPath(inst)} — nothing to patch (was this instance provisioned by the runner?)` };
|
|
917
917
|
}
|
|
918
918
|
try {
|
|
919
|
-
log(
|
|
920
|
-
boxWriteFile(webEnvPath(inst), upsertEnvVars(current, settingsEnvVarsFor(inst, current)), { mode: 0o600, sudo: true });
|
|
919
|
+
log(` [config] patch the settings lines of web.env in place (creds + origins untouched)${carry.carried.length ? `; carry the renamed env forward: ${carry.carried.join(', ')} (value copied on the box, never shown)` : ''}`);
|
|
920
|
+
boxWriteFile(webEnvPath(inst), upsertEnvVars(current, { ...settingsEnvVarsFor(inst, current), ...carry.vars }), { mode: 0o600, sudo: true });
|
|
921
921
|
log(' [service] restart so the memoized branding pack re-reads them');
|
|
922
922
|
pmFor(deps).restartService(inst, { exec: boxExec, sudoP });
|
|
923
923
|
} catch (e) {
|
|
@@ -960,7 +960,7 @@ async function settingsApplyInstance(inst, deps) {
|
|
|
960
960
|
if (provisioning.clearErrorNote) await provisioning.clearErrorNote(db, inst.id, 'settings-apply').catch(() => {});
|
|
961
961
|
await provisioning.recordEvent(db, {
|
|
962
962
|
instanceId: inst.id, ownerBuilderId: inst.owner_builder_id, event: 'settings-applied',
|
|
963
|
-
detail: `settings pushed (${shown}) — web.env patched in place; ${outcome}`,
|
|
963
|
+
detail: `settings pushed (${shown}) — web.env patched in place${carry.carried.length ? `; carried ${carry.carried.join(', ')}` : ''}; ${outcome}`,
|
|
964
964
|
actor: 'runner',
|
|
965
965
|
}).catch(() => {});
|
|
966
966
|
log(healthy && consumed !== false
|
|
@@ -1285,7 +1285,7 @@ async function cmdRunIntents(deps) {
|
|
|
1285
1285
|
// Dispatch EXPLICITLY per action — an unrecognized action must surface as an
|
|
1286
1286
|
// error, never fall through to the destructive teardown leg (task 1002692).
|
|
1287
1287
|
let result;
|
|
1288
|
-
if (intent.action === 'provision') result = await provisionInstance(inst, deps);
|
|
1288
|
+
if (intent.action === 'provision') result = await require('./provision-render.js').afterProvision(inst, deps, intent, await provisionInstance(inst, deps)); // then the app on Render, when the wizard lent a key (task 1004352)
|
|
1289
1289
|
else if (intent.action === 'teardown') result = await teardownInstance(inst, deps);
|
|
1290
1290
|
else if (intent.action === 'domain-attach') result = await domainAttachInstance(inst, deps);
|
|
1291
1291
|
else if (intent.action === 'settings-apply') result = await settingsApplyInstance(inst, deps);
|
|
@@ -1489,7 +1489,7 @@ module.exports = {
|
|
|
1489
1489
|
makeExec, makeWriteFile, allocatePort, boundLocalPorts, provisionInstance, teardownInstance,
|
|
1490
1490
|
domainAttachInstance, releasableDomains, cmdRunIntents, cmdReconcile, cmdStatus,
|
|
1491
1491
|
// live settings push (task 1003140) + the in-place env patch + read-back it rides on
|
|
1492
|
-
settingsApplyInstance, upsertEnvVars, settingsEnvVarsFor, settingsConsumed, instanceManifestCmd,
|
|
1492
|
+
settingsApplyInstance, upsertEnvVars, renamedEnvCarry, settingsEnvVarsFor, settingsConsumed, instanceManifestCmd,
|
|
1493
1493
|
// restart an unreachable ACTIVE instance (task 1003238) — the settings leg minus the env patch
|
|
1494
1494
|
restartInstance,
|
|
1495
1495
|
// GitHub App Manifest flow (task 2080, ADR 0133)
|
|
@@ -72,6 +72,7 @@ const PUBLISH_ALLOWLIST = [
|
|
|
72
72
|
'modules/provisioning/', // instance-hosting control plane — CORE (its do-api toolchain is core). Reclassified IN so the core CLI scripts/gds/provision.js resolves it from the core package post-split, not a MODULE_NOT_FOUND (task 2135). default:false — shipped ≠ enabled.
|
|
73
73
|
'modules/agents/', // the declarative agent registry + spawn seam (goal 1000038) — CORE: its own manifest declares maintenance.status 'core-maintained', and it consumes the core `grade` port, so a released core without it cannot run the MAS machinery the methodology describes. default:false — shipped is not enabled (ADR 0083), exactly like provisioning + platform-identity below. Omitted at module-creation time (tasks 1002485/1002486): the omission was invisible until tests/agents_spawn.mjs became the first PUBLISHABLE file to import it and the R89 boundary guard caught it (task 1002489).
|
|
74
74
|
'modules/npm-release/', // the deploy page's "where is the work" + version history (task 1002622). default:false — on only in the hall that publishes the core. MUST be here: modules/hall-ui/ ships as one subtree, so a released core without this carries deploy.js's loader and the #nr-work mounts with no routes behind them — the copy-desk asymmetry above.
|
|
75
|
+
'modules/render-deploy/', // the deploy page of a project whose app deploys through Render (task 1004270). default:false. MUST be here for the npm-release reason: modules/hall-ui/ ships as one subtree, so a released core without it carries deploy.js's loader for #rd-panel with no routes behind it.
|
|
75
76
|
'modules/platform-identity/', // hub identity provider (ADR 0141) — ships in core, enabled ONLY on the hub (cloudbongos.com). MUST be here or the hub's enabledModules() throws "unknown module" and provision.js's federation load MODULE_NOT_FOUNDs (task 1002235, same rationale as provisioning above). default:false — shipped ≠ enabled; a vanilla/self-hosted instance never turns it on.
|
|
76
77
|
// --- neutral (customer-0) config starters + their schemas (the filled-in *.json twins are denied) ---
|
|
77
78
|
'config/branding.neutral.json',
|
|
@@ -137,6 +137,25 @@ class RenderApi extends JsonHttpApi {
|
|
|
137
137
|
return this._req('POST', `/services/${encodeURIComponent(serviceId)}/deploys`, {}, { dryRunResult: { id: null } });
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// A service's deploys, newest first, as the bare deploy objects (Render wraps each in
|
|
141
|
+
// `{ deploy, cursor }`; both shapes are read). The list carries no owner, so the
|
|
142
|
+
// service is read through the guard first (the deploy hall's history, task 1004270).
|
|
143
|
+
async listDeploys(serviceId, { limit = 30 } = {}) {
|
|
144
|
+
await this.getService(serviceId);
|
|
145
|
+
const n = Math.max(1, Math.min(100, Number.parseInt(limit, 10) || 30));
|
|
146
|
+
const json = await this._req('GET', `/services/${encodeURIComponent(serviceId)}/deploys?limit=${n}`, null, { dryRunResult: { items: [] } });
|
|
147
|
+
const items = Array.isArray(json) ? json : (json && json.items) || [];
|
|
148
|
+
return items.map((e) => (e && e.deploy) || e).filter((d) => d && typeof d.id === 'string');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Roll the service back to one of its earlier deploys. The answer carries no owner, so
|
|
152
|
+
// the service is read through the guard first. This is a write on the owner's account;
|
|
153
|
+
// the caller (the deploy hall) asks the owner twice before it reaches here.
|
|
154
|
+
async rollbackTo(serviceId, deployId) {
|
|
155
|
+
await this.getService(serviceId);
|
|
156
|
+
return this._req('POST', `/services/${encodeURIComponent(serviceId)}/rollback`, { deployId }, { dryRunResult: { id: null } });
|
|
157
|
+
}
|
|
158
|
+
|
|
140
159
|
// ---- Postgres --------------------------------------------------------------
|
|
141
160
|
|
|
142
161
|
async createPostgres(payload) {
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
// scripts/gds/render-payload.js — WHAT the Render standup leg creates, kept apart from
|
|
2
|
-
// HOW it creates it (scripts/gds/provision-render.js) (task 1004183
|
|
2
|
+
// HOW it creates it (scripts/gds/provision-render.js) (task 1004183; retargeted by
|
|
3
|
+
// task 1004350).
|
|
3
4
|
//
|
|
4
|
-
// Pure functions, no I/O.
|
|
5
|
-
//
|
|
6
|
-
// the
|
|
7
|
-
//
|
|
8
|
-
//
|
|
5
|
+
// Pure functions, no I/O. What goes to the owner's Render account is their APP, not the
|
|
6
|
+
// Bongos hall: we host every project's hall, and the app deploys where its owner
|
|
7
|
+
// chooses (ADR 0345 D1, D3). So the service is built from the project's own repository
|
|
8
|
+
// and started the way that repository says — the owner's runtime and commands, or the
|
|
9
|
+
// repository's own Dockerfile — never the Bongos core.
|
|
9
10
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
11
|
+
// A database is optional (some apps need none). When there is one, its internal
|
|
12
|
+
// connection string is handed to the app as DATABASE_URL, the one name every mainstream
|
|
13
|
+
// framework reads. That is the only env var the platform sets: every other variable is
|
|
14
|
+
// the owner's to set on Render, and Bongos keeps their NAMES only (task 1004062's
|
|
15
|
+
// manifest) — never a value.
|
|
16
|
+
//
|
|
17
|
+
// The plans are per-project settings (ADR 0327 §5), never constants. The defaults are
|
|
18
|
+
// what the Render pilot (task 1004142) found enough at idle: a `starter` web service and
|
|
19
|
+
// a paid `basic_256mb` Postgres, because Render's free Postgres has no backups.
|
|
14
20
|
|
|
15
21
|
'use strict';
|
|
16
22
|
|
|
17
|
-
const crypto = require('node:crypto');
|
|
18
|
-
|
|
19
23
|
// The vocabulary lives with the web tier's validation, so the two can never disagree.
|
|
20
|
-
const { WEB_PLANS, DB_PLANS, REGIONS } = require('../../modules/provisioning/render-standup.js');
|
|
24
|
+
const { WEB_PLANS, DB_PLANS, REGIONS, RUNTIMES } = require('../../modules/provisioning/render-standup.js');
|
|
21
25
|
const DEFAULTS = Object.freeze({ webPlan: 'starter', dbPlan: 'basic_256mb', region: 'virginia' });
|
|
22
26
|
|
|
23
27
|
// A Render resource name from the project's slug. Render allows letters, digits and
|
|
@@ -27,7 +31,7 @@ function resourceName(inst, suffix) {
|
|
|
27
31
|
return suffix ? `${base}-${suffix}` : base;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
// The plan and region
|
|
34
|
+
// The plan and region a project asked for, with the defaults filled in. A value outside
|
|
31
35
|
// the vocabulary is refused rather than passed through for Render to reject.
|
|
32
36
|
function plansFor(appHost) {
|
|
33
37
|
const h = appHost || {};
|
|
@@ -42,35 +46,29 @@ function plansFor(appHost) {
|
|
|
42
46
|
return out;
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
// How the app is built and started, as the owner said. `docker` builds the repository's
|
|
50
|
+
// own Dockerfile and needs no commands; every other runtime needs a start command, and a
|
|
51
|
+
// build command if the app has a build step.
|
|
52
|
+
function buildFor(appHost) {
|
|
53
|
+
const h = appHost || {};
|
|
54
|
+
if (!RUNTIMES.includes(h.runtime)) throw new Error(`unknown Render runtime '${h.runtime || 'none'}'`);
|
|
55
|
+
if (h.runtime === 'docker') return { runtime: 'docker' };
|
|
56
|
+
if (!h.startCommand) throw new Error('the app needs a start command');
|
|
57
|
+
return { runtime: h.runtime, buildCommand: h.buildCommand || null, startCommand: h.startCommand };
|
|
58
|
+
}
|
|
59
|
+
|
|
45
60
|
// POST /postgres. `plan` is never 'free' — see the header.
|
|
46
61
|
function renderPostgresPayload(inst, { ownerId, dbPlan, region }) {
|
|
47
62
|
return { name: resourceName(inst, 'db'), ownerId, plan: dbPlan, region, version: '16' };
|
|
48
63
|
}
|
|
49
64
|
|
|
50
|
-
// POST /services, for
|
|
51
|
-
// internal connection string
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// - HOST=0.0.0.0: the server binds 127.0.0.1 otherwise and Render's router never reaches it;
|
|
58
|
-
// - PGDATABASE = the URL: migrate.sh addresses the database as `psql -d "$PGDATABASE"`;
|
|
59
|
-
// - BUILDER_SECRET_KEY: Render's disk is wiped each deploy, so the key-file fallback
|
|
60
|
-
// would mint a new key every time and orphan every stored per-builder secret;
|
|
61
|
-
// - no PORT: Render injects it.
|
|
62
|
-
// Bongos reads DATABASE_URL first (src/bongos/pool.js), so none of the PG* family the box
|
|
63
|
-
// writes is needed — the one simplification Render buys.
|
|
64
|
-
function renderServicePayload(inst, { ownerId, repoUrl, branch, webPlan, region, dbUrl, secretKey, envPrefix, firstAdmin }) {
|
|
65
|
-
const envVars = [
|
|
66
|
-
{ key: 'NODE_VERSION', value: '22' },
|
|
67
|
-
{ key: 'HOST', value: '0.0.0.0' },
|
|
68
|
-
{ key: 'DATABASE_URL', value: dbUrl },
|
|
69
|
-
{ key: 'PGDATABASE', value: dbUrl },
|
|
70
|
-
{ key: 'PGPOOL_MAX', value: '5' },
|
|
71
|
-
{ key: 'BUILDER_SECRET_KEY', value: secretKey },
|
|
72
|
-
];
|
|
73
|
-
if (firstAdmin) envVars.push({ key: `${envPrefix}_FIRST_ADMIN`, value: firstAdmin });
|
|
65
|
+
// POST /services, for the owner's app from their repository. `dbUrl` is the database's
|
|
66
|
+
// internal connection string, or null when the app has no database. It is a VALUE sent
|
|
67
|
+
// to Render and held there — the platform keeps none.
|
|
68
|
+
function renderServicePayload(inst, { ownerId, repoUrl, branch, webPlan, region, build, dbUrl }) {
|
|
69
|
+
const envSpecificDetails = build.runtime === 'docker'
|
|
70
|
+
? {}
|
|
71
|
+
: { ...(build.buildCommand ? { buildCommand: build.buildCommand } : {}), startCommand: build.startCommand };
|
|
74
72
|
return {
|
|
75
73
|
type: 'web_service',
|
|
76
74
|
name: resourceName(inst),
|
|
@@ -78,33 +76,9 @@ function renderServicePayload(inst, { ownerId, repoUrl, branch, webPlan, region,
|
|
|
78
76
|
repo: repoUrl,
|
|
79
77
|
branch: branch || 'main',
|
|
80
78
|
autoDeploy: 'yes',
|
|
81
|
-
serviceDetails: {
|
|
82
|
-
|
|
83
|
-
plan: webPlan,
|
|
84
|
-
region,
|
|
85
|
-
healthCheckPath: '/healthz',
|
|
86
|
-
envSpecificDetails: {
|
|
87
|
-
buildCommand: 'npm ci',
|
|
88
|
-
startCommand: 'node node_modules/@bongos/core/src/platform-server.js',
|
|
89
|
-
preDeployCommand: 'bash node_modules/@bongos/core/scripts/migrate.sh',
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
envVars,
|
|
79
|
+
serviceDetails: { runtime: build.runtime, plan: webPlan, region, envSpecificDetails },
|
|
80
|
+
envVars: dbUrl ? [{ key: 'DATABASE_URL', value: dbUrl }] : [],
|
|
93
81
|
};
|
|
94
82
|
}
|
|
95
83
|
|
|
96
|
-
|
|
97
|
-
// Render gave it. The four origins must all be that one host: a single-host instance has
|
|
98
|
-
// no separate builders./status. subdomain, and without them the hall never loads.
|
|
99
|
-
function renderAfterCreateEnv(serviceUrl, { envPrefix }) {
|
|
100
|
-
const origin = String(serviceUrl || '').replace(/\/+$/, '');
|
|
101
|
-
if (!/^https:\/\//.test(origin)) throw new Error(`Render gave the service no https URL (${serviceUrl || 'none'})`);
|
|
102
|
-
return ['PUBLIC', 'BUILDERS', 'STATUS', 'OAUTH'].map((k) => ({ key: `${envPrefix}_${k}_ORIGIN`, value: origin }));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function freshSecretKey() { return crypto.randomBytes(32).toString('hex'); }
|
|
106
|
-
|
|
107
|
-
module.exports = {
|
|
108
|
-
WEB_PLANS, DB_PLANS, REGIONS, DEFAULTS,
|
|
109
|
-
resourceName, plansFor, renderPostgresPayload, renderServicePayload, renderAfterCreateEnv, freshSecretKey,
|
|
110
|
-
};
|
|
84
|
+
module.exports = { DEFAULTS, resourceName, plansFor, buildFor, renderPostgresPayload, renderServicePayload };
|
|
@@ -102,6 +102,13 @@ const INTEGRATION = new Set([
|
|
|
102
102
|
// never was a dedicated consumer-layout-boot workflow, whatever older notes say.
|
|
103
103
|
'consumer_layout_boot',
|
|
104
104
|
'claim_gate_open_blocker', 'claim_gate_rebase',
|
|
105
|
+
// task 1003457: proves the blocker-resolve auto-promotion trigger (migration
|
|
106
|
+
// 009, tightened by core_223) fires on the HALL'S OWN write path — the real
|
|
107
|
+
// buildGdsRouter() HTTP routes, not a static SQL-text pin (blocker_resolve_guard.mjs,
|
|
108
|
+
// blocker_unlink.mjs) or a pure-predicate unit test (blocker_link.mjs, which never
|
|
109
|
+
// touches a database). Only a real transaction engine behind a real router proves
|
|
110
|
+
// that; self-skips without a reachable Postgres.
|
|
111
|
+
'blocker_hall_live_proof',
|
|
105
112
|
// task 1002709: core_221's re-keyed backstop index against a real Postgres.
|
|
106
113
|
// The DB-free sibling pins the guard and the migration's text; this proves the
|
|
107
114
|
// index actually refuses the commingling INSERT and still allows the parallel
|
|
@@ -122,6 +129,9 @@ const INTEGRATION = new Set([
|
|
|
122
129
|
// dev box session. The claims foreign key that failed the live upgrade is a
|
|
123
130
|
// schema fact only a transaction engine enforces. Always rolled back.
|
|
124
131
|
'core_258_box_session_claims_db',
|
|
132
|
+
// task 1004360: core_262 drops the four builders columns migration 104 added, against a real
|
|
133
|
+
// Postgres, with the foreign key live, then re-runs it. Always rolled back.
|
|
134
|
+
'core_262_drop_box_blocked_db',
|
|
125
135
|
'discord_autojoin', 'discord_oauth',
|
|
126
136
|
// task 1003000 (IC6): the three ideator credit streams against a real Postgres.
|
|
127
137
|
// The three DB-free sibling suites stub the client and SIMULATE the
|
|
@@ -14,7 +14,8 @@ const { cliClient, printOnboardingNudge, rewardPolicy, cardFlagsFromMe } = requi
|
|
|
14
14
|
const { closeoutPlan } = require('../../modules/lifecycle/ship-preflight');
|
|
15
15
|
const { syncMemoryOnShip } = require('./ship-memory-sync.js');
|
|
16
16
|
const { uploadSessionOnShip } = require('./ship-session-upload.js');
|
|
17
|
-
const {
|
|
17
|
+
const { execFileSync } = require('node:child_process');
|
|
18
|
+
const { gitOk } = require('./ship-git.js');
|
|
18
19
|
const { fetchDeployedVersion } = require('./ship-deploy-target.js');
|
|
19
20
|
const { landBailed, reconciledCardStatus } = require('./ship-land.js');
|
|
20
21
|
const { autoMerge, publishHeld } = require('./ship-merge.js');
|
|
@@ -142,18 +143,23 @@ async function onboardingNudgeBestEffort() {
|
|
|
142
143
|
// close-out, folded into the delivery process (NOT a separate skill). After a
|
|
143
144
|
// ship lands, scratch often lingers uncommitted — untracked files the preflight
|
|
144
145
|
// lets through, generated artifacts, post-commit edits — making the session
|
|
145
|
-
// hard to archive cleanly. This
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
146
|
+
// hard to archive cleanly. This REPORTS what's left and leaves it in place.
|
|
147
|
+
// task 1004254: it used to `git stash push -u` the leftover, but the stash stack
|
|
148
|
+
// is repo-global across every worktree (task 1003344), so one builder's scratch
|
|
149
|
+
// landed where a parallel agent's pop could apply it to the wrong branch — and
|
|
150
|
+
// it swallowed review renders an owner still needed. It never touches the stash.
|
|
151
|
+
// STRICTLY fail-open: the ship has already succeeded by the time this runs, so
|
|
152
|
+
// nothing here may throw, prompt, or change the exit status — a messy tree must
|
|
153
|
+
// never surface as a ship failure. The caller gates this on a SUCCESSFUL land.
|
|
152
154
|
function postShipCloseout(taskId) {
|
|
153
155
|
try {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
156
|
+
// Untrimmed: gitOk's trim() eats the first line's leading status column, so
|
|
157
|
+
// " M a.txt" was listed as ".txt" (task 1004254).
|
|
158
|
+
let out;
|
|
159
|
+
try {
|
|
160
|
+
out = execFileSync('git', ['status', '--porcelain'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
161
|
+
} catch (_) { return; } // git unavailable / not a repo — nothing to reconcile
|
|
162
|
+
const plan = closeoutPlan(out);
|
|
157
163
|
if (plan.clean) {
|
|
158
164
|
console.log('Close-out: working tree clean — session is archive-ready.');
|
|
159
165
|
return;
|
|
@@ -162,15 +168,7 @@ function postShipCloseout(taskId) {
|
|
|
162
168
|
console.log(`Close-out: ${plan.leftover.length} uncommitted/untracked path(s) remain after the ship:`);
|
|
163
169
|
for (const f of plan.leftover.slice(0, 15)) console.log(` ${f}`);
|
|
164
170
|
if (plan.leftover.length > 15) console.log(` … and ${plan.leftover.length - 15} more`);
|
|
165
|
-
|
|
166
|
-
// Label is `otb-postship-<numericId>` — no shell metacharacters.
|
|
167
|
-
const stashed = shellOk(`git stash push --include-untracked -m "${plan.stashLabel}"`);
|
|
168
|
-
if (stashed) {
|
|
169
|
-
console.log(`Close-out: stashed the leftover as "${plan.stashLabel}" — tree is now clean + archive-ready.`);
|
|
170
|
-
console.log(' Recover anytime: git stash list → git stash pop');
|
|
171
|
-
} else {
|
|
172
|
-
console.log('Close-out: could not stash the leftover; left it in place (the ship already succeeded — tidy up manually when convenient).');
|
|
173
|
-
}
|
|
171
|
+
console.log(`Close-out: left in place (task ${taskId} already shipped) — commit, move or delete them before archiving this worktree.`);
|
|
174
172
|
} catch (err) {
|
|
175
173
|
// Fail-open — a post-ship close-out problem must never look like a ship failure.
|
|
176
174
|
console.log(`Close-out: skipped (${err && err.message ? err.message : 'error'}).`);
|
|
@@ -404,6 +402,7 @@ module.exports = {
|
|
|
404
402
|
finishHeld,
|
|
405
403
|
mergeAndDeploy,
|
|
406
404
|
onboardingNudgeBestEffort,
|
|
405
|
+
postShipCloseout,
|
|
407
406
|
postShipSync,
|
|
408
407
|
printCompletionCard,
|
|
409
408
|
};
|
|
@@ -10,9 +10,12 @@
|
|
|
10
10
|
# 5. Promote A manually (the route allows it; the gate is at claim time)
|
|
11
11
|
# 6. Try to claim A — still 409 DEPS_NOT_SHIPPED
|
|
12
12
|
# 7. Claim B, ship B (active → completed → confirmed → shipped)
|
|
13
|
-
# 8. Verify
|
|
13
|
+
# 8. Verify C — a second dependent of B that NOTHING ever promoted by hand —
|
|
14
|
+
# flipped backlog→ready. This is the only assertion that can fail if the
|
|
15
|
+
# auto-promotion trigger is broken (task 1004191): A was already 'ready'
|
|
16
|
+
# from the manual step, so A's status proves nothing about the trigger.
|
|
14
17
|
# 9. Claim A — succeeds
|
|
15
|
-
# 10. Release A, abandon A (B is shipped and cannot be abandoned)
|
|
18
|
+
# 10. Release A, abandon A and C (B is shipped and cannot be abandoned)
|
|
16
19
|
#
|
|
17
20
|
# Run from any environment with a valid CLI session (/builder-setup).
|
|
18
21
|
# Requires curl.
|
|
@@ -44,7 +47,22 @@ A=$(curl -sS -X POST "$BASE/api/bongos/tasks" \
|
|
|
44
47
|
A_ID=$(echo "$A" | python3 -c "import json,sys; print(json.load(sys.stdin)['task']['id'])")
|
|
45
48
|
echo " A id=$A_ID"
|
|
46
49
|
|
|
47
|
-
echo "===
|
|
50
|
+
echo "=== 2b. Create task C (dependent, status=backlog, NEVER promoted by hand) ==="
|
|
51
|
+
# task 1004191 (finding 62 of the task 1000379 audit): A is promoted manually in
|
|
52
|
+
# step 4, so "A is ready after B ships" held whether or not the auto-promotion
|
|
53
|
+
# trigger fired. C exists to be the dependent only the trigger can flip.
|
|
54
|
+
C=$(curl -sS -X POST "$BASE/api/bongos/tasks" \
|
|
55
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
56
|
+
--data "{\"version_id\":\"GDS-V3\",\"title\":\"__smoke__ dep-test C $SUFFIX\",\"description\":\"smoke\",\"status\":\"backlog\",\"kind\":\"feature\",\"discipline\":\"engineering\",\"priority\":5,\"touches\":[\"smoke-dep-C-$SUFFIX/\"],\"source\":\"smoke\",\"source_ref\":\"smoke-dep-C-$SUFFIX\"}")
|
|
57
|
+
C_ID=$(echo "$C" | python3 -c "import json,sys; print(json.load(sys.stdin)['task']['id'])")
|
|
58
|
+
echo " C id=$C_ID"
|
|
59
|
+
|
|
60
|
+
echo "=== 3. Wire A → depends_on B, and C → depends_on B ==="
|
|
61
|
+
curl -sS -X POST "$BASE/api/bongos/tasks/$C_ID/dependencies" \
|
|
62
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
63
|
+
--data "{\"depends_on_task_id\": $B_ID}" >/dev/null
|
|
64
|
+
C_PRE=$(curl -sS -H "Authorization: Bearer $TOKEN" "$BASE/api/bongos/tasks/$C_ID" | python3 -c "import json,sys; print(json.load(sys.stdin)['task']['status'])")
|
|
65
|
+
[ "$C_PRE" = "backlog" ] && echo " PASS: C is backlog before B ships" || (echo " FAIL: C is '$C_PRE' before B ships — the auto-promotion check below would prove nothing"; exit 1)
|
|
48
66
|
curl -sS -X POST "$BASE/api/bongos/tasks/$A_ID/dependencies" \
|
|
49
67
|
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
50
68
|
--data "{\"depends_on_task_id\": $B_ID}" | python3 -m json.tool
|
|
@@ -69,15 +87,18 @@ curl -sS -X POST "$BASE/api/bongos/claims/by-task/$B_ID/resolve" \
|
|
|
69
87
|
curl -sS -X POST "$BASE/api/bongos/tasks/$B_ID/confirm" -H "Authorization: Bearer $TOKEN" >/dev/null
|
|
70
88
|
curl -sS -X POST "$BASE/api/bongos/tasks/$B_ID/ship" -H "Authorization: Bearer $TOKEN" >/dev/null
|
|
71
89
|
|
|
72
|
-
echo "=== 7. Verify
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
90
|
+
echo "=== 7. Verify the auto-promotion trigger fired: C backlog → ready ==="
|
|
91
|
+
# C was never promoted by hand, so only promote_dependents_on_ship can have made
|
|
92
|
+
# it 'ready'. (A is 'ready' from the manual step 4 regardless, so A is not asserted
|
|
93
|
+
# here — that is the assertion that used to pass with the trigger broken.)
|
|
94
|
+
C_STATUS=$(curl -sS -H "Authorization: Bearer $TOKEN" "$BASE/api/bongos/tasks/$C_ID" | python3 -c "import json,sys; print(json.load(sys.stdin)['task']['status'])")
|
|
95
|
+
echo " C status: $C_STATUS"
|
|
96
|
+
[ "$C_STATUS" = "ready" ] && echo " PASS: C auto-promoted" || (echo " FAIL: C is '$C_STATUS' — B shipped but the auto-promotion trigger did not flip its dependent"; exit 1)
|
|
76
97
|
|
|
77
98
|
echo "=== 8. Claim A — must succeed now ==="
|
|
78
99
|
node scripts/gds/claim.js "$A_ID" --worktree "smoke-$SUFFIX-A"
|
|
79
100
|
|
|
80
|
-
echo "=== 9. Cleanup — abandon A; B is shipped and stays ==="
|
|
101
|
+
echo "=== 9. Cleanup — abandon A and C; B is shipped and stays ==="
|
|
81
102
|
# task 1004026: this step used to ANNOUNCE "release A; mark both abandoned" and then
|
|
82
103
|
# only release A. Neither task reached a terminal state, so every run leaked two
|
|
83
104
|
# __smoke__ rows into the live tasks table — invisibly, because anyone grepping the
|
|
@@ -108,6 +129,14 @@ case "${A_CLEANUP_CODE:-000}" in
|
|
|
108
129
|
000) echo " SKIP cleanup — POST /tasks/$A_ID/abandon got no response; task #$A_ID left in the ledger" ;;
|
|
109
130
|
*) echo " SKIP cleanup — POST /tasks/$A_ID/abandon returned HTTP $A_CLEANUP_CODE; task #$A_ID left in the ledger" ;;
|
|
110
131
|
esac
|
|
132
|
+
C_CLEANUP_CODE=$(curl -sS -o /dev/null -w '%{http_code}' -X POST \
|
|
133
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
134
|
+
--data '{"reason":"smoke cleanup (task 1004191) - auto-promotion witness; the throwaway row has served its purpose"}' \
|
|
135
|
+
"$BASE/api/bongos/tasks/$C_ID/abandon") || true
|
|
136
|
+
case "${C_CLEANUP_CODE:-000}" in
|
|
137
|
+
2*) echo " PASS cleaned up throwaway task #$C_ID (abandoned)" ;;
|
|
138
|
+
*) echo " SKIP cleanup — POST /tasks/$C_ID/abandon returned HTTP ${C_CLEANUP_CODE:-000}; task #$C_ID left in the ledger" ;;
|
|
139
|
+
esac
|
|
111
140
|
echo " NOTE task #$B_ID is SHIPPED and is deliberately left in the ledger — the abandon route refuses shipped work (cannot_abandon_shipped)."
|
|
112
141
|
|
|
113
142
|
echo
|
package/scripts/gds/status.js
CHANGED
|
@@ -167,6 +167,14 @@ function shortTitle(title, max = 52) {
|
|
|
167
167
|
return t.length > max ? t.slice(0, max - 1) + '…' : t;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
// "area 6" for a working-area goal, else nothing (task 1004057). Read from the
|
|
171
|
+
// goal's working_area FIELD, never parsed from its title and never taken from
|
|
172
|
+
// sort_order or the id, which both look like it and are not.
|
|
173
|
+
function areaTag(g) {
|
|
174
|
+
const n = g && g.working_area;
|
|
175
|
+
return Number.isInteger(n) && n > 0 ? 'area ' + n : '';
|
|
176
|
+
}
|
|
177
|
+
|
|
170
178
|
// "1 confirmed, 2 backlog" — non-shipped tasks grouped by status, ordered.
|
|
171
179
|
function remainingSummary(remaining) {
|
|
172
180
|
if (remaining.length === 0) return 'none — all shipped ✓';
|
|
@@ -283,7 +291,7 @@ function renderStatusWidget(data, criterion) {
|
|
|
283
291
|
const rows = goals
|
|
284
292
|
? goals.map((g) => {
|
|
285
293
|
const ach = g.status === 'achieved';
|
|
286
|
-
const head = `<tr><td class="st-td" colspan="5" style="background:var(--color-background-secondary);font-weight:500;"><i class="ti ti-target" aria-hidden="true"></i> ${sesc(g.title)} <span class="st-sub">· ${ach ? '<span class="st-ok">achieved</span>' : sesc(g.status)}${g.criteria.length ? '' : ' · no criteria yet'}</span></td></tr>`;
|
|
294
|
+
const head = `<tr><td class="st-td" colspan="5" style="background:var(--color-background-secondary);font-weight:500;"><i class="ti ti-target" aria-hidden="true"></i> ${sesc(g.title)} <span class="st-sub">· ${areaTag(g) ? sesc(areaTag(g)) + ' · ' : ''}${ach ? '<span class="st-ok">achieved</span>' : sesc(g.status)}${g.criteria.length ? '' : ' · no criteria yet'}</span></td></tr>`;
|
|
287
295
|
return head + g.criteria.map(critRow).join('');
|
|
288
296
|
}).join('')
|
|
289
297
|
: data.criteria.map(critRow).join('');
|
|
@@ -336,7 +344,7 @@ function renderStatusMarkdown(data, criterion) {
|
|
|
336
344
|
const tableHeader = () => { lines.push('| | Criterion | Pos | Shipped | Remaining |'); lines.push('|---|---|---|---|---|'); };
|
|
337
345
|
if (goals) {
|
|
338
346
|
for (const g of goals) {
|
|
339
|
-
lines.push(`**🎯 ${g.title}** — ${g.status === 'achieved' ? 'achieved ✓' : g.status}${g.criteria.length ? '' : ' · no criteria yet'}`);
|
|
347
|
+
lines.push(`**🎯 ${g.title}** — ${areaTag(g) ? areaTag(g) + ' · ' : ''}${g.status === 'achieved' ? 'achieved ✓' : g.status}${g.criteria.length ? '' : ' · no criteria yet'}`);
|
|
340
348
|
if (g.criteria.length) { tableHeader(); for (const c of g.criteria) lines.push(critRow(c)); }
|
|
341
349
|
lines.push('');
|
|
342
350
|
}
|
|
@@ -383,7 +391,7 @@ function printAll(data) {
|
|
|
383
391
|
};
|
|
384
392
|
if (goals) {
|
|
385
393
|
for (const g of goals) {
|
|
386
|
-
console.log(` 🎯 ${g.title} [${g.status === 'achieved' ? 'achieved ✓' : g.status}]${g.criteria.length ? '' : ' (no criteria yet)'}`);
|
|
394
|
+
console.log(` 🎯 ${g.title} [${areaTag(g) ? areaTag(g) + ' · ' : ''}${g.status === 'achieved' ? 'achieved ✓' : g.status}]${g.criteria.length ? '' : ' (no criteria yet)'}`);
|
|
387
395
|
for (const c of g.criteria) printCrit(c);
|
|
388
396
|
if (g.criteria.length) console.log('');
|
|
389
397
|
}
|
|
@@ -523,4 +531,4 @@ if (require.main === module) {
|
|
|
523
531
|
});
|
|
524
532
|
}
|
|
525
533
|
|
|
526
|
-
module.exports = { parseArgs, resolveVersion, defaultedVersionNote, positionalRefNote, renderStatusWidget, renderStatusMarkdown };
|
|
534
|
+
module.exports = { parseArgs, resolveVersion, defaultedVersionNote, positionalRefNote, renderStatusWidget, renderStatusMarkdown, areaTag };
|
package/scripts/gds/upgrade.js
CHANGED
|
@@ -127,9 +127,9 @@ function restorePin(instanceDir, depString, fsImpl = fs) {
|
|
|
127
127
|
|
|
128
128
|
// ---- steps (each injectable for tests) --------------------------------------
|
|
129
129
|
|
|
130
|
-
// `ignore` is waived (it comes from — and is argued for in — claude-materialize.js materializeWillRewrite, task 1004122); every OTHER dirty path still halts the bump.
|
|
130
|
+
// `ignore` is waived (it comes from — and is argued for in — claude-materialize.js materializeWillRewrite, task 1004122); every OTHER dirty path still halts the bump. -uall (task 1004188): a bare status collapses a NEW untracked dir to one '?? dir/' entry the file-path waiver never matches.
|
|
131
131
|
function gitTreeClean(instanceDir, run = spawnSync, ignore = []) {
|
|
132
|
-
const r = run('git', ['status', '--porcelain'], { cwd: instanceDir, encoding: 'utf8' });
|
|
132
|
+
const r = run('git', ['status', '--porcelain', '-uall'], { cwd: instanceDir, encoding: 'utf8' });
|
|
133
133
|
if (!r || r.error || r.status !== 0) return { ok: false, reason: 'git status failed (not a git repo?)' };
|
|
134
134
|
const skip = new Set(ignore), all = (r.stdout || '').split('\n').filter((l) => l.trim());
|
|
135
135
|
const kept = all.filter((l) => !skip.has(l.slice(3).trim()));
|
|
@@ -22,7 +22,13 @@
|
|
|
22
22
|
//
|
|
23
23
|
// The 6-month delay alone does NOT scrub secrets/topology — redaction is mandatory and
|
|
24
24
|
// the gate is the backstop. Reuses the status-mirror-sync transport shape (--repo-dir /
|
|
25
|
-
// --push + a scoped deploy key
|
|
25
|
+
// --push + a scoped deploy key).
|
|
26
|
+
//
|
|
27
|
+
// PUBLISHING IS A MANUAL STEP. No workflow or schedule runs this script (there is
|
|
28
|
+
// no sync-public-mirror workflow) and nothing publishes the public mirror automatically.
|
|
29
|
+
// A person runs it by hand, e.g.
|
|
30
|
+
// node scripts/public-mirror-export.js --repo-dir <mirror-checkout> --push
|
|
31
|
+
// (a scheduled public release channel needs the owner's approval first).
|
|
26
32
|
//
|
|
27
33
|
// USAGE:
|
|
28
34
|
// node scripts/public-mirror-export.js --repo-dir <mirror-checkout> [--push]
|
|
@@ -73,6 +73,16 @@ const MODULE_GLOBS = {
|
|
|
73
73
|
// protected - ADR 0314 in a second costume. Ownership follows what the
|
|
74
74
|
// file IS, and what it is is the core read facade over ten modules.
|
|
75
75
|
'src/bongos/routes/me.js',
|
|
76
|
+
// The rest of the core route directory (task 1004116). It is a BLANKET
|
|
77
|
+
// protected glob, so every file in it is walled, but until now only the two
|
|
78
|
+
// entries above were authorable: sixteen live routes (auth, public,
|
|
79
|
+
// core-update, builders, ...) sat in no module's territory and no goal could
|
|
80
|
+
// author a task on them. They are the core HTTP surface that stayed in core
|
|
81
|
+
// while domain routes carved into modules/<key>/routes/, so they are kernel's.
|
|
82
|
+
// Kernel was already protected, so this changes no module's protected status;
|
|
83
|
+
// the me.js overlap the task feared is gone (task 1004110 moved it here too).
|
|
84
|
+
// The two explicit entries above stay for their reasons.
|
|
85
|
+
'src/bongos/routes/',
|
|
76
86
|
'server.js', 'config/',
|
|
77
87
|
|
|
78
88
|
// ADOPTED (task 1004111): a protected path owned by no module is authorable
|
|
@@ -340,6 +350,9 @@ const MODULE_GLOBS = {
|
|
|
340
350
|
// (tests/npm_release_*.mjs) because a default-off module's own tests/ is skipped
|
|
341
351
|
// by the unit runner. The /deploy page shell stays hall-ui's (one key per surface).
|
|
342
352
|
'npm-release': ['modules/npm-release/', 'tests/npm_release_*.mjs'],
|
|
353
|
+
// --- the render-deploy module (task 1004270): a Render project's deploy page. Tests live at
|
|
354
|
+
// the repo root (tests/render_deploy_*.mjs) for the same reason as npm-release above.
|
|
355
|
+
'render-deploy': ['modules/render-deploy/', 'tests/render_deploy_*.mjs'],
|
|
343
356
|
// --- instance provisioning: the web-tier REQUEST + STATE surface (module home)
|
|
344
357
|
// plus the control-plane runner CLI — a command travels with its domain (the
|
|
345
358
|
// lifecycle precedent). Covers the projects-surface work (goal
|
|
@@ -986,7 +986,9 @@ function mountInternalSurfaces(app) {
|
|
|
986
986
|
if (req.method !== 'GET' && req.method !== 'HEAD') return next();
|
|
987
987
|
if (!DEPLOY_PAGE_RE.test(path.posix.normalize(req.path))) return next();
|
|
988
988
|
let door = false;
|
|
989
|
-
|
|
989
|
+
// The page also exists where the render-deploy module is on: a Render project's own deploy
|
|
990
|
+
// page (task 1004270). Neither module on and the page still answers with Settings.
|
|
991
|
+
try { door = isModuleEnabled('provisioning') || isModuleEnabled('render-deploy'); } catch { door = false; }
|
|
990
992
|
if (door) return next();
|
|
991
993
|
return res.redirect(302, `${isBuildersHost(req) ? '' : '/builders'}/settings#software-update`);
|
|
992
994
|
});
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.
|
|
78
|
+
const CORE_VERSION = '1.20.2'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -255,6 +255,15 @@ module.exports = {
|
|
|
255
255
|
// second protected list — so a module reaches it via the doorway, not a deep
|
|
256
256
|
// core require (which the module-boundary fitness check would red). PERMANENT.
|
|
257
257
|
get goalScopeCheck() { return require('./bongos/goal-scope-check'); },
|
|
258
|
+
// cascadeDispatch — the cheapest-model-first cascade router (src/bongos/
|
|
259
|
+
// cascade-dispatch.js, task 1001691 / ADR 0101 Part 1). KERNEL (it composes
|
|
260
|
+
// no domain state — a pure ladder + an injected runner), grandfathered
|
|
261
|
+
// dormant core since System 3 (grading/CLAUDE.md's own note: "stays in
|
|
262
|
+
// src/gds/... nothing calls it but its mock unit test"). The lifecycle
|
|
263
|
+
// module's relevance/anti-hack judge (goal-task-relevance-judge.js) is its
|
|
264
|
+
// FIRST real caller, injecting grade.runSubagent (resolved via the `grade`
|
|
265
|
+
// port, never required directly) as the runner — never a deep core require.
|
|
266
|
+
get cascadeDispatch() { return require('./bongos/cascade-dispatch'); },
|
|
258
267
|
|
|
259
268
|
// === DOMAIN capabilities still on the doorway — SCHEDULED TO RELOCATE ======
|
|
260
269
|
// NOT kernel: these leak a domain's queries through the doorway (ADR 0091
|