@bongos/core 1.19.1072 → 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 +6 -2
- 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 +2 -1
- 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 +4 -0
- package/docs/modules-contract.md +13 -1
- package/docs/onboarding/diagrams/04-architecture.mmd +11 -22
- package/docs/onboarding/diagrams/README.md +3 -3
- package/docs/onboarding/diagrams/assertions.json +2 -22
- package/docs/onboarding/primer.md +9 -28
- package/docs/page-inventory.json +4 -1
- package/docs/page-readings.json +1032 -1077
- package/docs/recipes/render-pilot.md +29 -10
- package/migrations/core_257_module_store_registry.sql +87 -0
- package/migrations/core_258_drop_dev_box_tables.sql +55 -0
- package/modules/agents/module.json +1 -0
- package/modules/autonomy/module.json +1 -0
- package/modules/builder-settings/module.json +1 -0
- package/modules/builder-settings/render-prefs.js +4 -5
- package/modules/copy-desk/docx.js +78 -5
- package/modules/copy-desk/module.json +1 -0
- package/modules/copy-desk/routes/copy-desk.js +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 +84 -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
|
@@ -1197,14 +1197,9 @@
|
|
|
1197
1197
|
// unchanged. Deep links activate their group first, so scrollIntoView lands on
|
|
1198
1198
|
// a visible section.
|
|
1199
1199
|
|
|
1200
|
-
// The Dev box group (box panel, desktop app, pairing) exists only where the
|
|
1201
|
-
// dev-box module runs — a vanilla instance has no boxes to manage, so the
|
|
1202
|
-
// whole group (rail entry included) stays out rather than showing an empty
|
|
1203
|
-
// shell. RAIL_GROUPS derives from the items so a #dev-box anchor no-ops too.
|
|
1204
1200
|
const RAIL_ITEMS = [
|
|
1205
1201
|
{ id: 'account', label: 'Account' },
|
|
1206
1202
|
{ id: 'access', label: 'Access & keys' },
|
|
1207
|
-
...(window.OTB.moduleOn('dev-box') ? [{ id: 'devbox', label: 'Dev box' }] : []),
|
|
1208
1203
|
{ id: 'preferences', label: 'Preferences' },
|
|
1209
1204
|
{ id: 'sound', label: 'Sound' },
|
|
1210
1205
|
// task 1004296 — every project's version and its update; settings-software-update.js.
|
|
@@ -1213,7 +1208,7 @@
|
|
|
1213
1208
|
const RAIL_GROUPS = RAIL_ITEMS.map((i) => i.id).concat('extensions');
|
|
1214
1209
|
let rail = null;
|
|
1215
1210
|
|
|
1216
|
-
// task 2172 — stable per-panel anchors (/settings#cli, /settings#
|
|
1211
|
+
// task 2172 — stable per-panel anchors (/settings#cli, /settings#voices, …)
|
|
1217
1212
|
// so skills and docs can deep-link a panel without depending on internal
|
|
1218
1213
|
// section ids. #art-key predates the map and IS its section id (the hall's
|
|
1219
1214
|
// "Add your key" action + the CLI nudge target it) — kept verbatim.
|
|
@@ -1225,7 +1220,6 @@
|
|
|
1225
1220
|
'disciplines': 'craft-scroll',
|
|
1226
1221
|
'cli': 'cli-scroll',
|
|
1227
1222
|
'art-key': 'art-key',
|
|
1228
|
-
'dev-box-app': 'boxapp-scroll',
|
|
1229
1223
|
'interaction': 'interaction-scroll',
|
|
1230
1224
|
'wandering': 'wandering-scroll',
|
|
1231
1225
|
'rendering': 'render-scroll',
|
|
@@ -1341,19 +1335,12 @@
|
|
|
1341
1335
|
// DISPLAY NAME — the label beside your work (task 1669); seeded from /me.
|
|
1342
1336
|
setupDisplayName(builder);
|
|
1343
1337
|
|
|
1344
|
-
// DEV BOX APP — revealed once we know the viewer is a real builder,
|
|
1345
|
-
// and only where the dev-box module runs (the group's rail entry is gated
|
|
1346
|
-
// the same way above — task 2172).
|
|
1347
|
-
if (window.OTB.moduleOn('dev-box')) {
|
|
1348
|
-
$('#boxapp-scroll').hidden = false;
|
|
1349
|
-
loadDevboxLatestVersion(); // #924: best-effort "latest version: X" courtesy line
|
|
1350
|
-
}
|
|
1351
1338
|
// CONNECTIONS — Discord (hidden unless the server has Discord configured).
|
|
1352
1339
|
setupConnections(me && me.discord ? me.discord : null);
|
|
1353
1340
|
// CLI ACCESS — re-issue a CLI bearer from this browser session.
|
|
1354
1341
|
setupCliAccess();
|
|
1355
1342
|
// MODULE PANELS — render any Settings panels modules contributed via the
|
|
1356
|
-
// seam (BV1.R42). None today; ready for
|
|
1343
|
+
// seam (BV1.R42). None today; ready for discord/art once they leave core.
|
|
1357
1344
|
mountContributedPanels();
|
|
1358
1345
|
// Reveal the Extensions rail entry only when a module actually contributed:
|
|
1359
1346
|
// the kit rail re-renders from the item list, then re-marks the active group.
|
|
@@ -1383,27 +1370,10 @@
|
|
|
1383
1370
|
// the `needs` art_key item that drives the covered/action-needed banner.
|
|
1384
1371
|
await loadArtKey(me);
|
|
1385
1372
|
// Every section has now loaded/revealed — land any panel-anchor deep link
|
|
1386
|
-
// (/settings#cli, #
|
|
1373
|
+
// (/settings#cli, #voices, …) on its visible section.
|
|
1387
1374
|
applyHash();
|
|
1388
1375
|
}
|
|
1389
1376
|
|
|
1390
|
-
// #924: show the current published Dev Box app version next to the download
|
|
1391
|
-
// buttons, read from the same public endpoint the app polls (/downloads/devbox/
|
|
1392
|
-
// version → config/devbox-app.json). Best-effort: a fetch failure just leaves the
|
|
1393
|
-
// courtesy line hidden — it must never break the settings page.
|
|
1394
|
-
async function loadDevboxLatestVersion() {
|
|
1395
|
-
try {
|
|
1396
|
-
const r = await fetch('/downloads/devbox/version', { headers: { Accept: 'application/json' } });
|
|
1397
|
-
if (!r.ok) return;
|
|
1398
|
-
const v = await r.json();
|
|
1399
|
-
if (v && v.version) {
|
|
1400
|
-
const el = $('#boxapp-latest');
|
|
1401
|
-
el.textContent = `Latest version: v${v.version}.`;
|
|
1402
|
-
el.hidden = false;
|
|
1403
|
-
}
|
|
1404
|
-
} catch (_) { /* best-effort courtesy line — never break the page */ }
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
1377
|
wireRail();
|
|
1408
1378
|
load();
|
|
1409
1379
|
})();
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
"surface": "hall-ui",
|
|
5
5
|
"stub": { "prefix": "/builders/" },
|
|
6
6
|
"modeQuery": false,
|
|
7
|
-
"ignoreRequests": ["/api/(gds|bongos)/"
|
|
8
|
-
"ignoreConsole": ["/api/(gds|bongos)/", "
|
|
7
|
+
"ignoreRequests": ["/api/(gds|bongos)/"],
|
|
8
|
+
"ignoreConsole": ["/api/(gds|bongos)/", "\\b401\\b", "\\b404\\b", "Unauthorized", "Not Found", "load failed", "Password field"],
|
|
9
9
|
"states": {
|
|
10
10
|
"account": { "auth": true, "actions": [["wait", 900]], "expect": { "visible": ["#settings-h1", "#display-name-input", "#craft-chips", "#privacy-rows"] } },
|
|
11
11
|
"access": { "auth": true, "url": "/settings#access", "actions": [["wait", 900]], "expect": { "visible": ["#cli-reissue-btn", "#sessions-list", "#artkey-card"] } },
|
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
<script src="/builders/approval-queue.js"></script>
|
|
190
190
|
<!-- The page names and the draft body (the tweak editor's pure rules), then
|
|
191
191
|
the studio's own. -->
|
|
192
|
-
<script src="/builders/tweak-editor-lib.js?v=2026-09-28-
|
|
192
|
+
<script src="/builders/tweak-editor-lib.js?v=2026-09-28-tw23"></script>
|
|
193
193
|
<script src="/builders/studio-lib.js?v=2026-09-28-tw16"></script>
|
|
194
194
|
<script src="/builders/studio.js?v=2026-09-28-tw16"></script>
|
|
195
195
|
</body>
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
--z-drawer: 150;
|
|
231
231
|
/* A confirm dialog is the one thing that must sit above the drawer: it is
|
|
232
232
|
asking a question the drawer's content cannot answer. Added by task 1003307
|
|
233
|
-
because
|
|
233
|
+
because two pages had each guessed a bare number for exactly this
|
|
234
234
|
(200 and 50), which is what a missing layer looks like. */
|
|
235
235
|
--z-modal: 160;
|
|
236
236
|
--z-diagram-full: 9999;
|
|
@@ -1303,7 +1303,6 @@ html[data-fullscreen="on"] .fullscreen-toggle {
|
|
|
1303
1303
|
.stat-tile:first-child { border-left: 0; }
|
|
1304
1304
|
.stat-tile__label,
|
|
1305
1305
|
.gate-stat__label,
|
|
1306
|
-
.harbor-stat__label,
|
|
1307
1306
|
.watch-stat__label {
|
|
1308
1307
|
font-family: var(--label-face);
|
|
1309
1308
|
font-size: var(--label-size);
|
|
@@ -1313,7 +1312,6 @@ html[data-fullscreen="on"] .fullscreen-toggle {
|
|
|
1313
1312
|
}
|
|
1314
1313
|
.stat-tile__num,
|
|
1315
1314
|
.gate-stat__value,
|
|
1316
|
-
.harbor-stat__value,
|
|
1317
1315
|
.watch-stat__num {
|
|
1318
1316
|
font-family: var(--font-display);
|
|
1319
1317
|
font-size: clamp(26px, 2.6vw, 34px);
|
|
@@ -2433,11 +2431,6 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
|
|
|
2433
2431
|
.setup__tab:first-child { padding-left: 0; }
|
|
2434
2432
|
.setup__tab:hover { color: var(--ink); }
|
|
2435
2433
|
.setup__tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }
|
|
2436
|
-
.setup__tab-tag { font-family: var(--label-face); font-size: 10.5px; letter-spacing: var(--label-space); text-transform: var(--label-case); margin-left: 6px; color: var(--ink-faint); }
|
|
2437
|
-
.setup__tab-tag--rec { color: var(--ok); }
|
|
2438
|
-
.setup__pane { display: none; }
|
|
2439
|
-
.setup[data-config="desktop"] .setup__pane--desktop { display: block; }
|
|
2440
|
-
.setup[data-config="online"] .setup__pane--online { display: block; }
|
|
2441
2434
|
.setup__steps { margin: 8px 0; padding-left: 20px; font-size: 13.5px; }
|
|
2442
2435
|
.setup__steps li { margin: 6px 0; }
|
|
2443
2436
|
/* The setup panel's prose links ("Git", "Claude Code", "primer") take the
|
|
@@ -3512,6 +3505,11 @@ a.stat-tile:hover .stat-tile__sub { color: var(--ink-soft); }
|
|
|
3512
3505
|
.next-up__title { font-weight: 400; font-size: 14px; color: var(--ink); }
|
|
3513
3506
|
.next-up__title:hover { color: var(--accent-ink); }
|
|
3514
3507
|
.next-up__meta { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
|
|
3508
|
+
/* task 1003459: the row's Claim button reuses .task__claim's pill (the
|
|
3509
|
+
claimable card's own accent fill, style.css ~2769) rather than a second
|
|
3510
|
+
definition — align-self keeps it from stretching to the column's full
|
|
3511
|
+
width, which flex's default cross-axis stretch would otherwise do. */
|
|
3512
|
+
.next-up__item .task__claim { align-self: flex-start; margin-top: 4px; }
|
|
3515
3513
|
.next-up__foot { margin: 14px 0 18px; font-size: 13px; }
|
|
3516
3514
|
|
|
3517
3515
|
/* ---------- jump palette (task 2178) ----------
|
|
@@ -3638,16 +3636,16 @@ html.palette-open { overflow: hidden; }
|
|
|
3638
3636
|
state is a HAIRLINE in its own colour with no fill, and a scope is a quiet
|
|
3639
3637
|
mono chip. Neither is pressable, so neither takes the accent.
|
|
3640
3638
|
|
|
3641
|
-
These live here because
|
|
3642
|
-
.gov-pill, .
|
|
3643
|
-
|
|
3639
|
+
These live here because several pages had already invented them separately —
|
|
3640
|
+
.gov-pill, .gate-pill, .badge, .mod-chip — each re-spelling the label voice
|
|
3641
|
+
as literals. The shell had no neutral pill to take: .chip below is
|
|
3644
3642
|
the accent-tinted FILTER chip, and it means "press me".
|
|
3645
3643
|
|
|
3646
3644
|
The state colours are the hall's fixed verification vocabulary, never the
|
|
3647
3645
|
world's warm accent states (ADR 0196 §2): a status must not re-tint when an
|
|
3648
3646
|
instance re-brands. */
|
|
3649
3647
|
/* The names below are the SAME pill, found by the task 1003307 audit:
|
|
3650
|
-
government,
|
|
3648
|
+
government, gate and watch each invented one because the shell had
|
|
3651
3649
|
none to take (modules had one too; its rows take .fact-pill by name since
|
|
3652
3650
|
task 1003472). They are aliased rather than renamed because renaming would
|
|
3653
3651
|
mean editing the JS row builders, and a class name is not what was wrong —
|
|
@@ -3655,7 +3653,6 @@ html.palette-open { overflow: hidden; }
|
|
|
3655
3653
|
in their own sheets. */
|
|
3656
3654
|
.fact-pill,
|
|
3657
3655
|
.gov-pill,
|
|
3658
|
-
.harbor-pill,
|
|
3659
3656
|
.gate-pill,
|
|
3660
3657
|
.badge {
|
|
3661
3658
|
display: inline-flex;
|
|
@@ -69,6 +69,9 @@
|
|
|
69
69
|
<script src="/builders/brand-apply.js?v=2026-06-19-r56" defer></script>
|
|
70
70
|
<script src="/builders/md-reader.js" defer></script>
|
|
71
71
|
<script src="/builders/hall-visual.js?v=2026-09-16-visual1" defer></script>
|
|
72
|
-
|
|
72
|
+
<!-- task 1003459: the hall's ONE claim/release action (task 1003458) — this
|
|
73
|
+
page's Claim it button consumes it rather than a second hand-rolled POST. -->
|
|
74
|
+
<script src="/builders/claim-action.js?v=2026-09-26-claimaction1" defer></script>
|
|
75
|
+
<script src="/builders/task.js?v=2026-09-28-taskclaim" defer></script>
|
|
73
76
|
</body>
|
|
74
77
|
</html>
|
|
@@ -142,6 +142,15 @@
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
// task 1003459 — the hall's ONE claim action (task 1003458), consumed here so
|
|
146
|
+
// a task's own page can claim it directly instead of only handing over the
|
|
147
|
+
// copyable `/builder-claim N` string. `load` is hoisted below; reusing it as
|
|
148
|
+
// the refresh means a successful (or 409-refused) claim re-renders the whole
|
|
149
|
+
// record from the server's own state, the same idiom every other write here
|
|
150
|
+
// already follows.
|
|
151
|
+
const { makeClaimAction } = window.OTBClaimAction;
|
|
152
|
+
const claimAction = makeClaimAction({ postJSON, toast, API, refresh: load });
|
|
153
|
+
|
|
145
154
|
// task 759: run a lifecycle action (ship / confirm), then reload so the page
|
|
146
155
|
// re-renders at the new status. On failure, surface the server's refusal and
|
|
147
156
|
// re-enable the button. `load` is hoisted below.
|
|
@@ -711,7 +720,16 @@
|
|
|
711
720
|
const ownsLastClaim = !!(viewer && lastClaim && lastClaim.github_login &&
|
|
712
721
|
lastClaim.github_login === viewer.github_login);
|
|
713
722
|
let lifecycle = '';
|
|
714
|
-
|
|
723
|
+
// task 1003459 — the same door the Work board and Goals page already open.
|
|
724
|
+
// `ready` is the one status the claim gate can actually admit (backlog
|
|
725
|
+
// needs a promote first, blocked waits on a dependency, and both already
|
|
726
|
+
// say so in the lifecycle rail above) — every other refusal (rank, an
|
|
727
|
+
// owed rebase, a module/touches conflict, …) is still possible and still
|
|
728
|
+
// surfaces, because claimAction.claim() carries the same friendlyClaimError
|
|
729
|
+
// switch the board uses, not a second guess at it here.
|
|
730
|
+
if (t.status === 'ready') {
|
|
731
|
+
lifecycle = `<button class="td-btn td-btn--primary" type="button" id="claim-btn">Claim it</button>`;
|
|
732
|
+
} else if (t.status === 'confirmed' && (isSupervisor || ownsLastClaim)) {
|
|
715
733
|
lifecycle = `<button class="td-btn td-btn--primary" type="button" id="ship-btn">Ship it</button>`;
|
|
716
734
|
} else if (t.status === 'completed') {
|
|
717
735
|
lifecycle = rank === 'archon'
|
|
@@ -823,6 +841,23 @@
|
|
|
823
841
|
});
|
|
824
842
|
}
|
|
825
843
|
|
|
844
|
+
const claimBtn = $('#claim-btn');
|
|
845
|
+
if (claimBtn) {
|
|
846
|
+
claimBtn.addEventListener('click', async () => {
|
|
847
|
+
if (claimBtn.disabled) return;
|
|
848
|
+
claimBtn.disabled = true;
|
|
849
|
+
claimBtn.textContent = '…';
|
|
850
|
+
const out = await claimAction.claim(t.id);
|
|
851
|
+
// A redirect to sign-in is already navigating away; re-enabling here
|
|
852
|
+
// would just invite a click that can't land (task.js's own pattern,
|
|
853
|
+
// runLifecycle above).
|
|
854
|
+
if (!out.ok && !out.redirected) {
|
|
855
|
+
claimBtn.disabled = false;
|
|
856
|
+
claimBtn.textContent = 'Claim it';
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
|
|
826
861
|
const shipBtn = $('#ship-btn');
|
|
827
862
|
if (shipBtn) {
|
|
828
863
|
shipBtn.addEventListener('click', () => {
|
|
@@ -107,6 +107,23 @@
|
|
|
107
107
|
return `uploaded · ${n} ${n === 1 ? 'line' : 'lines'} from your doc`;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// A refused upload, as the panel says it (task 1004354): a headline naming
|
|
111
|
+
// what happened, the next step in plain words, and whether a fresh download
|
|
112
|
+
// is part of that step. The server's own what_this_means sits between them.
|
|
113
|
+
const REDOWNLOAD = 'Or download this page again, make your changes in the new file, and upload that.';
|
|
114
|
+
const UPLOAD_REFUSALS = Object.freeze({
|
|
115
|
+
wrong_page: { title: 'This file is a different page.', next: `Pick the file you downloaded from this page and upload it again. ${REDOWNLOAD}`, download: true },
|
|
116
|
+
reading_moved: { title: 'This page changed after you downloaded it.', next: 'Download this page again, copy your changes into the new file, and upload that.', download: true },
|
|
117
|
+
structure_changed: { title: 'Some lines in your file moved.', next: `Put the lines listed here back the way they were, one paragraph per line, and upload again. ${REDOWNLOAD}`, download: true },
|
|
118
|
+
not_a_docx: { title: 'That file is not a Word file.', next: 'Upload the .docx you downloaded here. From Google Docs, choose File, then Download, then Microsoft Word (.docx).', download: false },
|
|
119
|
+
upload_too_large: { title: 'That file is too big.', next: `Take out any pictures you pasted in and upload it again. ${REDOWNLOAD}`, download: true },
|
|
120
|
+
page_not_held: { title: 'Someone else is writing this page now.', next: 'Reload the editor to see who holds it. Your file is still on your computer.', download: false },
|
|
121
|
+
page_in_flight: { title: 'This page has already been submitted.', next: 'Reload the editor. Its words are frozen until it ships or is sent back.', download: false },
|
|
122
|
+
});
|
|
123
|
+
function uploadRefusal(code) {
|
|
124
|
+
return UPLOAD_REFUSALS[code] || { title: 'Your file was not saved.', next: `Try the upload again. ${REDOWNLOAD}`, download: true };
|
|
125
|
+
}
|
|
126
|
+
|
|
110
127
|
// A page's name as the artist reads it ("Home", "Projects"). The inventory's
|
|
111
128
|
// title is the page's <title>, and on the apex that is a branding template
|
|
112
129
|
// ("{{productName}}: {{tagline}}"), which is not a name. So a templated title
|
|
@@ -131,7 +148,7 @@
|
|
|
131
148
|
return PAGE_ID_RE.test(id) ? id : null;
|
|
132
149
|
}
|
|
133
150
|
|
|
134
|
-
const api = { NOTES, sectionsOf, isChanged, noteFor, draftBody, changedCount, counterText, initialValues, rowsFor, liveUrl, uploadedLabel, displayTitle, pageIdFrom };
|
|
151
|
+
const api = { NOTES, sectionsOf, isChanged, noteFor, draftBody, changedCount, counterText, initialValues, rowsFor, liveUrl, uploadedLabel, uploadRefusal, displayTitle, pageIdFrom };
|
|
135
152
|
if (typeof window !== 'undefined') window.OTBTweakEditor = api;
|
|
136
153
|
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
137
154
|
})();
|
|
@@ -404,6 +404,22 @@ html:not([data-fullscreen="on"]) .tw-bar { top: calc(var(--topbar-h, 56px) + 12p
|
|
|
404
404
|
.tw-stage .tw-linkish:focus-visible { outline: 2px solid var(--tw-accent); outline-offset: 2px; }
|
|
405
405
|
.tw-done__error { margin: 0; font-size: 14px; line-height: 1.5; color: var(--tw-link); }
|
|
406
406
|
|
|
407
|
+
/* A REFUSED UPLOAD (task 1004354). Not on the artboard, which has no failing
|
|
408
|
+
state: the "is in" panel's own glass and scrim, so a refusal is as
|
|
409
|
+
unmissable as a submit. It scrolls inside itself on a short phone rather
|
|
410
|
+
than hide its buttons below the window. */
|
|
411
|
+
.tw-refused .tw-done__panel { max-height: calc(100vh - 32px); overflow-y: auto; }
|
|
412
|
+
.tw-refused__list {
|
|
413
|
+
margin: 0;
|
|
414
|
+
padding-left: 18px;
|
|
415
|
+
font-size: 14px;
|
|
416
|
+
line-height: 1.5;
|
|
417
|
+
color: var(--tw-ink-soft);
|
|
418
|
+
}
|
|
419
|
+
.tw-refused__list li { margin: 2px 0; overflow-wrap: anywhere; }
|
|
420
|
+
.tw-refused__list[hidden] { display: none; }
|
|
421
|
+
.tw-refused__next { margin: 0; font-size: 15px; line-height: 1.5; color: var(--tw-ink); }
|
|
422
|
+
|
|
407
423
|
/* PHONE WIDTH (not on the artboard, which is 1440 wide). The bar stops being
|
|
408
424
|
one pill and becomes a glass card of two rows: where you are and how far,
|
|
409
425
|
then the four doors, wrapping. The paper takes the width, and each margin
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
12
12
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
13
13
|
<link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
|
|
14
|
-
<link rel="stylesheet" href="/builders/tweak-editor.css?v=2026-09-28-
|
|
14
|
+
<link rel="stylesheet" href="/builders/tweak-editor.css?v=2026-09-28-tw23">
|
|
15
15
|
</head>
|
|
16
16
|
<!-- THE TWEAK EDITOR (task 1004321 / BV2.TW10, goal 1000095; ADR 0341 D4 and
|
|
17
17
|
D11). docs/design/mocks/tweak-mode/Editor.dc.html is the master design:
|
|
@@ -93,6 +93,29 @@
|
|
|
93
93
|
<div class="tw-picks" id="tw-picks" hidden></div>
|
|
94
94
|
</div>
|
|
95
95
|
</div>
|
|
96
|
+
|
|
97
|
+
<!-- A refused upload (task 1004354): the "is in" panel's glass over the
|
|
98
|
+
whole window, so it cannot scroll away. What happened, the lines
|
|
99
|
+
it names, and what to do. The notice on the paper stays after it
|
|
100
|
+
closes. -->
|
|
101
|
+
<div class="tw-done tw-refused" id="tw-refused" hidden>
|
|
102
|
+
<div class="tw-done__panel" role="alertdialog" aria-modal="true" aria-labelledby="tw-refused-title" aria-describedby="tw-refused-what">
|
|
103
|
+
<div class="tw-done__head">
|
|
104
|
+
<span class="tw-next__label">nothing was saved</span>
|
|
105
|
+
<h2 class="tw-done__title" id="tw-refused-title" tabindex="-1">Your file was not saved.</h2>
|
|
106
|
+
<p class="tw-done__sub" id="tw-refused-what"></p>
|
|
107
|
+
</div>
|
|
108
|
+
<ul class="tw-refused__list" id="tw-refused-list" hidden></ul>
|
|
109
|
+
<div class="tw-next">
|
|
110
|
+
<span class="tw-next__label">what to do</span>
|
|
111
|
+
<p class="tw-refused__next" id="tw-refused-next"></p>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="tw-done__acts">
|
|
114
|
+
<button class="tw-take" id="tw-refused-download" type="button">download this page again</button>
|
|
115
|
+
<button class="tw-linkish" id="tw-refused-close" type="button">close</button>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
96
119
|
</main>
|
|
97
120
|
|
|
98
121
|
<footer class="app-foot" id="app-foot"></footer>
|
|
@@ -105,7 +128,7 @@
|
|
|
105
128
|
<script src="/builders/shell.js?v=2026-09-28-archive"></script>
|
|
106
129
|
<script src="/builders/palette.js" defer></script>
|
|
107
130
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
108
|
-
<script src="/builders/tweak-editor-lib.js?v=2026-09-28-
|
|
109
|
-
<script src="/builders/tweak-editor.js?v=2026-09-28-
|
|
131
|
+
<script src="/builders/tweak-editor-lib.js?v=2026-09-28-tw23"></script>
|
|
132
|
+
<script src="/builders/tweak-editor.js?v=2026-09-28-tw23"></script>
|
|
110
133
|
</body>
|
|
111
134
|
</html>
|
|
@@ -244,6 +244,34 @@
|
|
|
244
244
|
const msg = (d && d.what_this_means) || errMessage(data) || fallback;
|
|
245
245
|
const items = refusalItems(d || {});
|
|
246
246
|
notice(`<p>${escapeHtml(msg)}</p>${items.length ? `<ul>${items.map((i) => `<li>${escapeHtml(i)}</li>`).join('')}</ul>` : ''}`);
|
|
247
|
+
return { msg, items };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// A refused UPLOAD also opens the panel over the whole window (task
|
|
251
|
+
// 1004354): an artist uploads from the bar, often far down the page, where a
|
|
252
|
+
// notice at the top of the paper is off screen. It names what happened and
|
|
253
|
+
// the next step; the paper's notice stays after it closes.
|
|
254
|
+
function showRefused(data, said) {
|
|
255
|
+
const code = data && data.error ? data.error.code : null;
|
|
256
|
+
const r = lib.uploadRefusal(code);
|
|
257
|
+
$('tw-refused-title').textContent = r.title;
|
|
258
|
+
$('tw-refused-what').textContent = said.msg;
|
|
259
|
+
const list = $('tw-refused-list');
|
|
260
|
+
list.innerHTML = said.items.map((i) => `<li>${escapeHtml(i)}</li>`).join('');
|
|
261
|
+
list.hidden = !said.items.length;
|
|
262
|
+
$('tw-refused-next').textContent = r.next;
|
|
263
|
+
$('tw-refused-download').hidden = !r.download;
|
|
264
|
+
$('tw-bar').inert = true;
|
|
265
|
+
$('tw-paper').inert = true;
|
|
266
|
+
$('tw-refused').hidden = false;
|
|
267
|
+
$('tw-refused-title').focus();
|
|
268
|
+
}
|
|
269
|
+
function closeRefused() {
|
|
270
|
+
if ($('tw-refused').hidden) return;
|
|
271
|
+
$('tw-refused').hidden = true;
|
|
272
|
+
$('tw-bar').inert = false;
|
|
273
|
+
$('tw-paper').inert = false;
|
|
274
|
+
$('tw-upload').focus();
|
|
247
275
|
}
|
|
248
276
|
|
|
249
277
|
// Leaving with an unsaved change: one last save that outlives the page.
|
|
@@ -294,7 +322,7 @@
|
|
|
294
322
|
btn.disabled = false;
|
|
295
323
|
if (!r.ok) {
|
|
296
324
|
btn.textContent = 'upload .docx';
|
|
297
|
-
refusal(r.data, 'The file could not be read. Nothing was saved.');
|
|
325
|
+
showRefused(r.data, refusal(r.data, 'The file could not be read. Nothing was saved.'));
|
|
298
326
|
return;
|
|
299
327
|
}
|
|
300
328
|
notice('');
|
|
@@ -403,6 +431,7 @@
|
|
|
403
431
|
document.addEventListener('focusout', (ev) => {
|
|
404
432
|
if (ev.target && ev.target.classList && ev.target.classList.contains('tw-line')) flush();
|
|
405
433
|
});
|
|
434
|
+
document.addEventListener('keydown', (ev) => { if (ev.key === 'Escape') closeRefused(); });
|
|
406
435
|
window.addEventListener('resize', () => { document.querySelectorAll('textarea.tw-line').forEach(fit); });
|
|
407
436
|
|
|
408
437
|
document.addEventListener('click', async (ev) => {
|
|
@@ -411,6 +440,8 @@
|
|
|
411
440
|
if (t.closest('#tw-upload')) { $('tw-file').click(); return; }
|
|
412
441
|
if (t.closest('#tw-submit')) { submit(); return; }
|
|
413
442
|
if (t.closest('#tw-take')) { takeNext(); return; }
|
|
443
|
+
if (t.closest('#tw-refused-close')) { closeRefused(); return; }
|
|
444
|
+
if (t.closest('#tw-refused-download')) { closeRefused(); download(); return; }
|
|
414
445
|
if (t.closest('#tw-claim')) {
|
|
415
446
|
const b = t.closest('#tw-claim');
|
|
416
447
|
b.disabled = true;
|
|
@@ -606,7 +606,7 @@
|
|
|
606
606
|
}
|
|
607
607
|
|
|
608
608
|
// Editors and the Claude desktop/CLI app autolink any bare "#NNN" in a session
|
|
609
|
-
// transcript to the
|
|
609
|
+
// transcript to the checkout's GitHub remote (…/issues/NNN), which 404s because
|
|
610
610
|
// our records number tasks separately from GitHub. So the scroll we hand the
|
|
611
611
|
// builder must never carry a bare "#NNN": write "task NNN" + the clickable hall
|
|
612
612
|
// URL, and strip "#NNN" out of the (free-text) task description too.
|
|
@@ -25,6 +25,7 @@ The paper is paper and the room is the room whatever the hall's theme: the stage
|
|
|
25
25
|
- **A focused line gets a hairline accent ring** beside the artboard's warm wash (the hall's focus rule, `tests/hall_audit.mjs` §10).
|
|
26
26
|
- **"pick another" opens the other pages in place** as chips under the actions (the studio artboard's own "pick another"), where the editor artboard linked to the studio, whose flow panel is TW16's.
|
|
27
27
|
- **Boxes grow to their words** rather than the artboard's rows 1 or 2.
|
|
28
|
+
- **A refused upload opens a panel** (task 1004354, `#tw-refused`): the "is in" panel's glass and scrim over the whole window, a headline per refusal (`lib.uploadRefusal`), the server's words, the lines it names, "what to do", and "download this page again" or close (Escape too). An artist uploads from the bar, often far down the page, where the paper's notice was off screen. The notice stays on the paper after the panel closes. The kit has no file-upload action, so the panel is proven in `tests/hall_tweak_editor.mjs` and by shots of a real browser at 1440 and 390, not by a states.json state.
|
|
28
29
|
- **Not on the artboard at all:** the phone layout (the bar becomes a two-row glass card, notes drop under their lines), the shell's full-screen button top right, the read-only and refusal notices on the paper, the chrome view (the switch off), and light mode.
|
|
29
30
|
|
|
30
31
|
## The proof
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"key": "ideas",
|
|
3
3
|
"title": "Ideas",
|
|
4
4
|
"description": "The idea inbox (capture, triage, criterion-proposal ratification) and the blockers queue — the methodology surfaces that feed the backlog. A core-domain module carved out of the GDS core in BV1.R78 (ADR 0093 §1); default-on because every instance captures ideas and tracks what's stuck on its owner. Registers the `ideas.capture` kernel PORT the discord #ideas inbound resolves (ADR 0093 §5 — retiring the temporary `captureIdea` doorway leak).",
|
|
5
|
+
"version": "1.0.0",
|
|
5
6
|
"coreVersion": "^1.8.0",
|
|
6
7
|
"default": true,
|
|
7
8
|
"maintenance": {
|
|
@@ -34,61 +34,6 @@ async function getActiveClaimsForBuilder(builderId) {
|
|
|
34
34
|
return rows;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
// Task-scoped dev box (goal 1000051 / R92, ADR 0148 §2). The set-union of the
|
|
38
|
-
// module KEYS a builder's active claims put in scope: each active claim's task →
|
|
39
|
-
// its goal → goal.scope_modules (the ADR 0086 scope wall), unioned + deduped
|
|
40
|
-
// across all the builder's active claims. This is the "what code does this
|
|
41
|
-
// builder's current work need" signal the dev-box source-access resolver
|
|
42
|
-
// (modules/dev-box/box-access.js, wired in R93/R94) maps to git sparse-checkout
|
|
43
|
-
// globs via the doorway `moduleScopeMap`. Returns [] when the builder holds no
|
|
44
|
-
// active claim, or when every claimed task carries no goal / an empty
|
|
45
|
-
// scope_modules — the box then falls back to BASE-only (the pre-existing Bongos
|
|
46
|
-
// instance). KEYS only; glob resolution is the box module's job (it reaches
|
|
47
|
-
// moduleScopeMap through the doorway, never a core-internal require — the
|
|
48
|
-
// R90/ADR-0148 boundary). Honours the #538 deps.pool test seam
|
|
49
|
-
// (NODE_ENV==='test' only), like the goals reads above.
|
|
50
|
-
async function moduleScopeForActiveClaims(builderId, deps = {}) {
|
|
51
|
-
const activePool = (deps.pool && process.env.NODE_ENV === 'test') ? deps.pool : pool;
|
|
52
|
-
const id = Number(builderId);
|
|
53
|
-
if (!Number.isInteger(id)) {
|
|
54
|
-
throw new Error('moduleScopeForActiveClaims: builderId must be numeric');
|
|
55
|
-
}
|
|
56
|
-
const { rows } = await activePool.query(
|
|
57
|
-
`SELECT DISTINCT m AS module_key
|
|
58
|
-
FROM claims c
|
|
59
|
-
JOIN tasks t ON t.id = c.task_id
|
|
60
|
-
JOIN goals g ON g.id = t.goal_id
|
|
61
|
-
CROSS JOIN LATERAL unnest(g.scope_modules) AS m
|
|
62
|
-
WHERE c.builder_id = $1 AND c.released_at IS NULL
|
|
63
|
-
ORDER BY m`,
|
|
64
|
-
[id]
|
|
65
|
-
);
|
|
66
|
-
return rows.map((r) => r.module_key);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Task 1002729 — the companion signal moduleScopeForActiveClaims cannot carry.
|
|
70
|
-
// That query INNER JOINs goals and unnests scope_modules, so it returns [] for
|
|
71
|
-
// BOTH "this builder holds no claim" and "this builder holds claims whose goals
|
|
72
|
-
// declare no scope_modules". Those must not be treated alike: the first is a
|
|
73
|
-
// genuinely unscoped box (BASE-only is right), the second is a builder actively
|
|
74
|
-
// working who would be handed a box with no modules/ at all. Live evidence for
|
|
75
|
-
// why this matters: 247 of 248 source grants on the cloudbongos fleet resolved to
|
|
76
|
-
// BASE, including an Archon holding 7 active claims — every one of those claims
|
|
77
|
-
// sat on a goal with scope_modules = {}. Counts CLAIMS, deliberately not joining
|
|
78
|
-
// goals, so it stays true regardless of how the goal wall is (or isn't) declared.
|
|
79
|
-
async function activeClaimCountForBuilder(builderId, deps = {}) {
|
|
80
|
-
const activePool = (deps.pool && process.env.NODE_ENV === 'test') ? deps.pool : pool;
|
|
81
|
-
const id = Number(builderId);
|
|
82
|
-
if (!Number.isInteger(id)) {
|
|
83
|
-
throw new Error('activeClaimCountForBuilder: builderId must be numeric');
|
|
84
|
-
}
|
|
85
|
-
const { rows } = await activePool.query(
|
|
86
|
-
`SELECT count(*)::int AS n FROM claims WHERE builder_id = $1 AND released_at IS NULL`,
|
|
87
|
-
[id]
|
|
88
|
-
);
|
|
89
|
-
return rows.length ? Number(rows[0].n) : 0;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
37
|
// task 1642 — every active claim bound to a given CLI session. "Active" is the
|
|
93
38
|
// claims-table convention (released_at IS NULL); there is NO `status` column on
|
|
94
39
|
// claims (a common misread — the task's status lives on `tasks`). Keyed on
|
|
@@ -159,7 +104,7 @@ async function getLastClaimHolder(taskId) {
|
|
|
159
104
|
// reading it.
|
|
160
105
|
//
|
|
161
106
|
// `deps.pool` is the family's test-only pool seam (task 538), honoured ONLY under
|
|
162
|
-
// NODE_ENV==='test' (the same line
|
|
107
|
+
// NODE_ENV==='test' (the same line the family's other reads use), so
|
|
163
108
|
// tests/grade_diff_hash.mjs can drive the lift with a fake row instead of a
|
|
164
109
|
// real Postgres (task 1003490). Production callers pass the task id alone.
|
|
165
110
|
async function getTaskGrade(taskId, deps = {}) {
|
|
@@ -217,12 +162,10 @@ async function getTaskSessionLogs(taskId) {
|
|
|
217
162
|
}
|
|
218
163
|
|
|
219
164
|
module.exports = {
|
|
220
|
-
activeClaimCountForBuilder,
|
|
221
165
|
getActiveClaimsForBuilder,
|
|
222
166
|
getActiveClaimsForSession,
|
|
223
167
|
getLastClaimHolder,
|
|
224
168
|
getTaskClaims,
|
|
225
169
|
getTaskGrade,
|
|
226
170
|
getTaskSessionLogs,
|
|
227
|
-
moduleScopeForActiveClaims,
|
|
228
171
|
};
|
|
@@ -672,11 +672,6 @@ async function shipTask({ taskId, builderId, landAssurance }, deps = {}) {
|
|
|
672
672
|
console.log('[gds] graduation broadcast post-ship failed:', e && e.message);
|
|
673
673
|
}
|
|
674
674
|
}
|
|
675
|
-
if (rankGraduation) {
|
|
676
|
-
await seams.emitAsync('builder.box-reconcile-needed', {
|
|
677
|
-
builderId: actualShipperId, rank: 'thetes', status: rankGraduation.status, actor: 'auto-graduation:#692',
|
|
678
|
-
}).catch(() => {});
|
|
679
|
-
}
|
|
680
675
|
await recordShipAssurance({ insertAuditLog, taskId, builderId: actualShipperId, assurance: shipAssuranceValue, log });
|
|
681
676
|
return {
|
|
682
677
|
...shipRow,
|
package/modules/lifecycle/db.js
CHANGED
|
@@ -203,14 +203,12 @@ const {
|
|
|
203
203
|
} = require('./db-overrides.js');
|
|
204
204
|
const { applyGrade } = require('./db-grade.js');
|
|
205
205
|
const {
|
|
206
|
-
activeClaimCountForBuilder,
|
|
207
206
|
getActiveClaimsForBuilder,
|
|
208
207
|
getActiveClaimsForSession,
|
|
209
208
|
getLastClaimHolder,
|
|
210
209
|
getTaskClaims,
|
|
211
210
|
getTaskGrade,
|
|
212
211
|
getTaskSessionLogs,
|
|
213
|
-
moduleScopeForActiveClaims,
|
|
214
212
|
} = require('./db-claim-reads.js');
|
|
215
213
|
const {
|
|
216
214
|
claimTask,
|
|
@@ -333,8 +331,6 @@ module.exports = {
|
|
|
333
331
|
getClaimPublishedSha, // task 1547 — read the published tip for the reconcile gate
|
|
334
332
|
shippedTasksByPublishedSha, // task 1004090 / BV2.R15 — the reverse read, for removal attribution
|
|
335
333
|
getActiveClaimsForBuilder,
|
|
336
|
-
moduleScopeForActiveClaims, // goal 1000051 / R92 — dev-box task-scoped source access (ADR 0148 §2)
|
|
337
|
-
activeClaimCountForBuilder, // task 1002729 — distinguishes "no claim" from "claim with an empty goal wall"
|
|
338
334
|
getActiveClaimsForSession,
|
|
339
335
|
getLastClaimHolder, // --regrade ownership guard (#678)
|
|
340
336
|
getTaskGrade, // V4.R08 — diff-hash reuse check before grading
|
|
@@ -107,10 +107,6 @@ function moduleKeySteps(spec) {
|
|
|
107
107
|
tasks.push({ title: 'Provide Discord bot token + webhooks', kind: 'infra',
|
|
108
108
|
description: 'The discord module needs a bot token + webhook URLs + DISCORD_BOT_ENABLED=true. Note: outbound/link surfaces fire on credentials even if the module flag is later turned off.' });
|
|
109
109
|
}
|
|
110
|
-
if (enabled.has('dev-box')) {
|
|
111
|
-
tasks.push({ title: 'Provide a DigitalOcean API token for dev boxes', kind: 'infra',
|
|
112
|
-
description: 'The dev-box module needs a DigitalOcean API token (and optionally Cloudflare) on the operator control plane before any builder box can be provisioned.' });
|
|
113
|
-
}
|
|
114
110
|
return tasks;
|
|
115
111
|
}
|
|
116
112
|
|
|
@@ -168,12 +168,4 @@ module.exports = {
|
|
|
168
168
|
// analytics.js (the AI-dev-curve): the builder + ecosystem analytics.
|
|
169
169
|
builderAnalytics: (...a) => db.builderAnalytics(...a),
|
|
170
170
|
ecosystemAnalytics: (...a) => db.ecosystemAnalytics(...a),
|
|
171
|
-
// dev-box (modules/dev-box/routes/box.js, via resolveOptional('lifecycle')): the
|
|
172
|
-
// module-key scope of a builder's active claims — the task-scoped source-access
|
|
173
|
-
// resolver (goal 1000051 / R92, ADR 0148 §2). Keys only; box-access maps them to
|
|
174
|
-
// git sparse-checkout globs through the doorway `moduleScopeMap`.
|
|
175
|
-
moduleScopeForActiveClaims: (...a) => db.moduleScopeForActiveClaims(...a),
|
|
176
|
-
// task 1002729 — the "is this builder actually working right now" signal that
|
|
177
|
-
// lets box-access tell an unscoped box from a crippled one (see db.js).
|
|
178
|
-
activeClaimCountForBuilder: (...a) => db.activeClaimCountForBuilder(...a),
|
|
179
171
|
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"key": "lifecycle",
|
|
3
3
|
"title": "Lifecycle (the build state machine)",
|
|
4
4
|
"description": "The build state machine \u2014 tasks \u00b7 claims \u00b7 versions \u00b7 done-when criteria \u00b7 goals \u00b7 dependencies \u00b7 the claim\u2192ship\u2192grade\u2192credit lifecycle \u00b7 override requests \u00b7 the ship/confirm/promote orchestration \u00b7 github-push + merge-lease + conflict-resolve + the publish reconciler. The LAST and most-coupled core domain carved out of the GDS core in BV1.R86 (ADR 0093 \u00a71,\u00a73, 02-module-map \u00a74 \u2014 kept whole, never split). default-on; every instance runs the lifecycle. Registers the `lifecycle` kernel PORT (createTask \u00b7 VOTABLE_TASK_STATUSES \u00b7 classifyTaskKind \u00b7 tallyPeerVotes + the work-tracking reads) that core read routes + the ideas/discord/autonomy modules resolve. As a transaction-participant it RESOLVES the `reward` + `onboarding` ports inside its claim/ship/confirm txns \u2014 but via resolveOptional (graceful, like sessions\u2192reward), so consumes is empty: a vanilla instance without those modules degrades the credit/onboarding writes to best-effort no-ops, it does not fail.",
|
|
5
|
+
"version": "1.0.0",
|
|
5
6
|
"coreVersion": "^1.12.0",
|
|
6
7
|
"default": true,
|
|
7
8
|
"maintenance": {
|
|
@@ -1115,12 +1115,11 @@ module.exports = function buildTasksRouter() {
|
|
|
1115
1115
|
// using its own push credential — a GitHub App installation token (preferred,
|
|
1116
1116
|
// task 1028) or the GITHUB_PUSH_TOKEN PAT fallback; single repo, Contents:write
|
|
1117
1117
|
// + PR:write + Actions:read — it CANNOT push to branch-protected main nor bypass
|
|
1118
|
-
// required checks. The
|
|
1119
|
-
// preserved.
|
|
1118
|
+
// required checks. The uploading checkout holds NO GitHub credential, so the
|
|
1119
|
+
// #919 model is preserved.
|
|
1120
1120
|
//
|
|
1121
|
-
// AUTH: requireBuilder + gateTaskOwnership
|
|
1122
|
-
//
|
|
1123
|
-
// session first). The grade already ran client-side against this exact HEAD
|
|
1121
|
+
// AUTH: requireBuilder + gateTaskOwnership. The grade already ran client-side
|
|
1122
|
+
// against this exact HEAD
|
|
1124
1123
|
// (ship.js grades BEFORE this call), and the bundle-tip == claimed head_sha
|
|
1125
1124
|
// check below binds the pushed code to that graded sha.
|
|
1126
1125
|
const publishUpload = express.json({ limit: PUBLISH_BODY_LIMIT });
|
|
@@ -1352,8 +1351,7 @@ module.exports = function buildTasksRouter() {
|
|
|
1352
1351
|
// ADR-0058 gate-review BLOCKS a Metic+ author's gate-surface PR shipped from a
|
|
1353
1352
|
// laptop and they can't self-approve their own PR (hit live on task 1162 / PR #131).
|
|
1354
1353
|
//
|
|
1355
|
-
// AUTH mirrors publish-branch EXACTLY: requireBuilder + gateTaskOwnership
|
|
1356
|
-
// deliberately NOT allowBoxScope (a box-scoped session 403s — it must reauth). The
|
|
1354
|
+
// AUTH mirrors publish-branch EXACTLY: requireBuilder + gateTaskOwnership. The
|
|
1357
1355
|
// attestation power is bounded: gate-author-trust clears a NEEDS_TRUST surface for
|
|
1358
1356
|
// a Metic+ author only and NEVER the hard floor (which always needs an owner
|
|
1359
1357
|
// review), so a Metic+ vouching for their own task's head stays within their tier.
|