@bongos/core 1.24.26 → 1.24.27
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 +142 -92
- package/docs/adr/0307-autobongos-a-deterministic-supervisor-and-agentic-workers.md +12 -0
- package/docs/copy-inventory.md +67 -48
- package/docs/copy-registry.json +279 -78
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +10 -4
- package/docs/page-readings.json +4335 -4314
- package/docs/recipes/ui-look-before-you-ship.md +1 -0
- package/modules/autonomy/fence.js +16 -1
- package/modules/government/protected-surfaces.json +2 -0
- package/modules/hall-ui/public/ai-studio.html +1 -1
- 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/city.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/commands.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/core-move-notice.js +9 -6
- package/modules/hall-ui/public/deploy-handoff.js +203 -0
- package/modules/hall-ui/public/deploy.html +4 -2
- package/modules/hall-ui/public/deploy.js +7 -1
- package/modules/hall-ui/public/diagrams.html +1 -1
- 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-map.html +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/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/market-watch-item.html +1 -1
- package/modules/hall-ui/public/market-watch-rules.html +1 -1
- package/modules/hall-ui/public/market-watch-settings.html +1 -1
- package/modules/hall-ui/public/market-watch.html +1 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/my-agents.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +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-software-update.js +22 -8
- package/modules/hall-ui/public/settings.html +7 -5
- package/modules/hall-ui/public/shell.js +25 -73
- package/modules/hall-ui/public/street.html +1 -1
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/style.css +119 -39
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/tweak-editor.html +1 -1
- package/modules/hall-ui/public/update-pill.js +623 -0
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/hall-ui/records/update-pill.md +12 -0
- package/modules/lifecycle/github-push.js +1 -0
- package/modules/lifecycle/routes/tasks.js +2 -2
- package/modules/lifecycle/runner-self-guard.js +122 -0
- package/modules/lifecycle/runner-self.js +154 -0
- package/modules/public-landing/public/account.html +3 -3
- package/modules/public-landing/public/assets/cosmos.css +45 -0
- package/modules/public-landing/public/community.html +4 -4
- package/modules/public-landing/public/connections.html +1 -1
- package/modules/public-landing/public/guild.html +1 -1
- package/modules/public-landing/public/invitations.html +4 -4
- package/modules/public-landing/public/projects.html +26 -26
- package/modules/status-ui/public/index.html +2 -2
- package/modules/status-ui/public/index.states.json +12 -0
- package/modules/status-ui/public/status.js +10 -2
- package/modules/status-ui/public/style.css +57 -0
- package/modules/ui-design/kit/README.md +1 -1
- package/modules/ui-design/kit/serve.js +8 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +14 -0
- package/scripts/bongos/autobongos-loop.js +4 -0
- package/scripts/bongos/autobongos-run.js +21 -4
- package/scripts/bongos/autobongos-tweak-lane.js +5 -3
- package/scripts/bongos/ship-flow.js +20 -0
- package/src/module-api.js +1 -1
- package/tests/autobongos_host_access.mjs +5 -3
- package/tests/autobongos_self_modification.mjs +420 -0
- package/tests/core_update_banner.mjs +6 -92
- package/tests/core_upgrade_owner_door.mjs +4 -56
- package/tests/deploy_handoff.mjs +206 -0
- package/tests/government_protected_surfaces.mjs +15 -0
- package/tests/hub_connections_page.mjs +1 -1
- package/tests/hub_invitations_page.mjs +1 -1
- package/tests/loading_mark.mjs +203 -0
- package/tests/nav_permission_atoms.mjs +3 -2
- package/tests/software_update.mjs +6 -2
- package/tests/update_pill.mjs +431 -0
|
@@ -1057,7 +1057,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1057
1057
|
<!-- signed-in: the list -->
|
|
1058
1058
|
<section id="mine" hidden>
|
|
1059
1059
|
<div class="alert" id="pageAlert" role="alert"></div>
|
|
1060
|
-
<div class="loading" id="loading">Reading your projects…</div>
|
|
1060
|
+
<div class="loading loading-mark loading-mark--page" id="loading" role="status">Reading your projects…</div>
|
|
1061
1061
|
|
|
1062
1062
|
<!-- search + state filter. The filter GROUPS the chip outcomes; it never
|
|
1063
1063
|
relabels them: "Live" holds both `live` and `live?` because neither is
|
|
@@ -1152,7 +1152,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1152
1152
|
</section>
|
|
1153
1153
|
|
|
1154
1154
|
<div class="alert" id="mAlert" role="alert"></div>
|
|
1155
|
-
<div class="loading" id="mLoading">Reading the project…</div>
|
|
1155
|
+
<div class="loading loading-mark loading-mark--page" id="mLoading" role="status">Reading the project…</div>
|
|
1156
1156
|
|
|
1157
1157
|
<section id="mMain" hidden>
|
|
1158
1158
|
<!-- the ledger of facts (task 1003345): each fact keeps its id and fills
|
|
@@ -1237,7 +1237,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1237
1237
|
|
|
1238
1238
|
<div class="card mSec" id="mPublish" hidden>
|
|
1239
1239
|
<h2>On the public map</h2>
|
|
1240
|
-
<div id="mPub"><p class="finePrint">Reading the project…</p></div>
|
|
1240
|
+
<div id="mPub"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p></div>
|
|
1241
1241
|
<details class="finePrint" id="mPubWhat">
|
|
1242
1242
|
<summary>What puts a project on the map</summary>
|
|
1243
1243
|
<p>Two things, and only two: a name — this project already has one — and a description of what it is. Modules, artwork, a finished setup interview: none of them are required, because the bar is there to keep empty shells off the map, not to make you finish anything. Until it is met the project is not listed publicly and nobody can ask to join it; the project itself runs exactly the same either way. Adding a description puts it on the map, and clearing one takes it back off.</p>
|
|
@@ -1251,7 +1251,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1251
1251
|
project) — the same call the card's fold used to make. -->
|
|
1252
1252
|
<div class="card mSec" id="mAddress" hidden>
|
|
1253
1253
|
<h2>Its address</h2>
|
|
1254
|
-
<div id="mDom"><p class="finePrint">Reading the project…</p></div>
|
|
1254
|
+
<div id="mDom"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p></div>
|
|
1255
1255
|
<details class="finePrint" id="mDomWhat">
|
|
1256
1256
|
<summary>Where an address comes from</summary>
|
|
1257
1257
|
<p>Every project gets one on this platform, at its own handle. You can point a domain you already own at it instead: type the name here, then add a DNS record for it at your registrar. The platform holds the address for this project and wires the secure connection once the name resolves. Changing it later lets the old address go, and nothing on the project restarts for it.</p>
|
|
@@ -1265,7 +1265,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1265
1265
|
(walk finding N4, ADR 0176). -->
|
|
1266
1266
|
<div class="card mSec" id="mRepoVisibility">
|
|
1267
1267
|
<h2>Repository visibility</h2>
|
|
1268
|
-
<div id="mRepoVis"><p class="finePrint">Checking the repository on GitHub…</p></div>
|
|
1268
|
+
<div id="mRepoVis"><p class="finePrint loading-mark loading-mark--inline" role="status">Checking the repository on GitHub…</p></div>
|
|
1269
1269
|
<details class="finePrint" id="mRepoVisWhy">
|
|
1270
1270
|
<summary>What making it private does</summary>
|
|
1271
1271
|
<p>The platform first gives this project its own read-only deploy key and switches its deploys over to it, and only then makes the repository private — in that order, so no deploy ever tries to read a private repository with a key that can’t. The change is made with your connected GitHub account, so that account needs admin rights on the repository. It works for a project on a Cloud host; a project on your own host is yours to change on GitHub directly. You can make it public again from here at any time.</p>
|
|
@@ -1281,7 +1281,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1281
1281
|
#mRepoVisWhy shape) so no re-render can drop it. -->
|
|
1282
1282
|
<div class="card mSec" id="mVisibility">
|
|
1283
1283
|
<h2>Who can see it</h2>
|
|
1284
|
-
<div id="mVis"><p class="finePrint">Reading the project’s settings…</p></div>
|
|
1284
|
+
<div id="mVis"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project’s settings…</p></div>
|
|
1285
1285
|
<details class="finePrint" id="mVisWhat">
|
|
1286
1286
|
<summary>What “only signed-in members” changes</summary>
|
|
1287
1287
|
<p>Visitors who aren’t signed in meet a sign-in door instead of the project’s pages — its front page, its status page, and its hall. The door itself stays open, so members can always get in, and members see everything either way. It is separate from the repository’s visibility above: the code on GitHub is its own setting. Changing it restarts the project to pick it up — the line under the choice says what the project is running right now.</p>
|
|
@@ -1296,7 +1296,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1296
1296
|
fold is a SIBLING of the volatile slot so no re-render can drop it. -->
|
|
1297
1297
|
<div class="card mSec" id="mJoinability">
|
|
1298
1298
|
<h2>How people join</h2>
|
|
1299
|
-
<div id="mJoin"><p class="finePrint">Reading the project’s settings…</p></div>
|
|
1299
|
+
<div id="mJoin"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project’s settings…</p></div>
|
|
1300
1300
|
<details class="finePrint" id="mJoinWhat">
|
|
1301
1301
|
<summary>What each choice changes</summary>
|
|
1302
1302
|
<p><b>Anyone can join</b> — whoever signs in with GitHub becomes a member at once, at the newcomer rank. <b>Apply to join</b> — people ask from the project’s hall, you approve each one from the hall’s Watch page, and then they sign in. <b>Invite only</b> — no one can apply: the project’s hall and its sign-in say so, and you invite people by GitHub username from the hall’s Watch page; only an invited person’s sign-in gets in — and people who asked before you switched stay in your queue, to approve or dismiss. Whichever you pick, people who are already members stay members. Changing it restarts the project to pick it up — the line under the choice says what the project is running right now.</p>
|
|
@@ -1315,7 +1315,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1315
1315
|
slot so no re-render can drop it. -->
|
|
1316
1316
|
<div class="card mSec" id="mDoorState">
|
|
1317
1317
|
<h2>On the map</h2>
|
|
1318
|
-
<div id="mDoor"><p class="finePrint">Reading the project’s settings…</p></div>
|
|
1318
|
+
<div id="mDoor"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project’s settings…</p></div>
|
|
1319
1319
|
<details class="finePrint" id="mDoorWhat">
|
|
1320
1320
|
<summary>What each choice changes</summary>
|
|
1321
1321
|
<p><b>Public</b> — the project is on the map under its name, and anyone who finds it can join. <b>Private</b> — it is on the map under its name, but nobody joins on their own: people ask, and you decide. <b>Stealth</b> — the map shows only that something is there; its name, and everything else about it, stay yours, and nobody can ask to join. Switching to Stealth is itself visible: someone comparing the map before and after can tell which project went quiet. Whichever you pick, a project reaches the map only once it has a name and a description. This is separate from “Who can see it” above — that one is the door on the project’s own pages. Changing it restarts the project to pick it up.</p>
|
|
@@ -1337,7 +1337,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1337
1337
|
SIBLING of the volatile slot so no re-render can drop it. -->
|
|
1338
1338
|
<div class="card mSec" id="mKind">
|
|
1339
1339
|
<h2>Its kind</h2>
|
|
1340
|
-
<div id="mKindPick"><p class="finePrint">Reading the project…</p></div>
|
|
1340
|
+
<div id="mKindPick"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p></div>
|
|
1341
1341
|
<details class="finePrint" id="mKindWhat">
|
|
1342
1342
|
<summary>What the choice changes</summary>
|
|
1343
1343
|
<p>How this project is listed — the kind shown beside its name on the map, in the table and on its card. It is a declaration about the project, not a setting the project obeys: nothing is pushed to it and nothing restarts. One thing does follow the kind: if you never picked your extras yourself, they are the set this kind suggests, so changing the kind changes them too — you will be told when that happens. Extras you picked for yourself are stored and never move. Not Sure is a real answer, and it is the way back if you would rather not say.</p>
|
|
@@ -1357,7 +1357,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1357
1357
|
is a SIBLING of the volatile slot so no re-render can drop it. -->
|
|
1358
1358
|
<div class="card mSec" id="mModules">
|
|
1359
1359
|
<h2>Its extras</h2>
|
|
1360
|
-
<div id="mMods"><p class="finePrint">Reading the project…</p></div>
|
|
1360
|
+
<div id="mMods"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p></div>
|
|
1361
1361
|
<details class="finePrint" id="mModsWhat">
|
|
1362
1362
|
<summary>What an extra is — and what is always on</summary>
|
|
1363
1363
|
<p>Extras are the optional parts a project can run — extra AI agents, a Discord mirror, and the rest. You picked a set when you created the project, or took the suggested set by skipping that step; this is where you change your mind. Switching one on or off saves straight away and changes nothing that is already running: nothing restarts, and no work is lost either way. The parts every project runs — the platform itself, the way it tracks work, the builders’ hall — were never part of the choice, so they are not listed here and cannot be switched off.</p>
|
|
@@ -1372,7 +1372,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1372
1372
|
environment variables with no value box is otherwise read as unfinished. -->
|
|
1373
1373
|
<div class="card mSec" id="mEnvVars">
|
|
1374
1374
|
<h2>Its environment variables</h2>
|
|
1375
|
-
<div id="mEnv"><p class="finePrint">Reading the project…</p></div>
|
|
1375
|
+
<div id="mEnv"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p></div>
|
|
1376
1376
|
<details class="finePrint" id="mEnvWhat">
|
|
1377
1377
|
<summary>Why there is nowhere to type the value</summary>
|
|
1378
1378
|
<p>This is a list of which variables your project needs and what each one is for — not the secrets themselves. The values belong wherever your project actually runs (Vercel, Netlify, Render, or your own machine): that is where the running app reads them, and it already keeps them encrypted, logs who touched them and lets you rotate them. Keeping a second copy here would make this platform the keeper of your credentials and buy you nothing.</p>
|
|
@@ -1425,7 +1425,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1425
1425
|
can drop it. -->
|
|
1426
1426
|
<div class="card mSec" id="mPlanetPick">
|
|
1427
1427
|
<h2>Its planet</h2>
|
|
1428
|
-
<div id="mPick"><p class="finePrint">Reading the project…</p></div>
|
|
1428
|
+
<div id="mPick"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p></div>
|
|
1429
1429
|
<details class="finePrint" id="mPickWhat">
|
|
1430
1430
|
<summary>What the choice changes</summary>
|
|
1431
1431
|
<p>How this project is drawn: on the sky, in your list, at the top of this page and on its card. A planet is a <b>shape</b> (the blob, the bongo pair, the bead), a <b>material</b> (chrome, frost, gold, obsidian), a <b>style</b> (tinted: the colour replaces the material’s own; washed: the material lit by the colour; plain: as it is) and a <b>colour</b>, one of the world’s five. Until you choose, the platform picks one for it. Saving here changes nothing on the project itself: no restart, nothing to apply.</p>
|
|
@@ -1437,7 +1437,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1437
1437
|
like it: nothing is pushed to the project and nothing restarts. -->
|
|
1438
1438
|
<div class="card mSec" id="mCard">
|
|
1439
1439
|
<h2>Its card</h2>
|
|
1440
|
-
<div id="mCardPick"><p class="finePrint">Reading the project…</p></div>
|
|
1440
|
+
<div id="mCardPick"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p></div>
|
|
1441
1441
|
<details class="finePrint" id="mCardWhat">
|
|
1442
1442
|
<summary>What the choice changes</summary>
|
|
1443
1443
|
<p>The backdrop behind this project’s name on its card — the panel that opens when someone picks it out of the map. Four plates the platform drew, or the project’s own picture if you have given it one. Saving here changes nothing on the project itself: no restart, nothing to apply.</p>
|
|
@@ -1452,7 +1452,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1452
1452
|
<div class="card mSec" id="mLook">
|
|
1453
1453
|
<h2>Its look</h2>
|
|
1454
1454
|
<div id="mLookBody">
|
|
1455
|
-
<div id="mLookPick"><p class="finePrint">Reading the project…</p></div>
|
|
1455
|
+
<div id="mLookPick"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p></div>
|
|
1456
1456
|
<div class="actions" id="mLookActions" hidden><button type="button" class="btn small" id="mLookSave">Save the look</button></div>
|
|
1457
1457
|
<div class="projMsg" id="mLookMsg" role="status" aria-live="polite"></div>
|
|
1458
1458
|
</div>
|
|
@@ -1472,7 +1472,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1472
1472
|
something, and the card's one-click confirm is too light for it. -->
|
|
1473
1473
|
<div class="card mSec" id="mDelete">
|
|
1474
1474
|
<h2>Take it offline</h2>
|
|
1475
|
-
<div id="mDel"><p class="finePrint">Reading the project…</p></div>
|
|
1475
|
+
<div id="mDel"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p></div>
|
|
1476
1476
|
<!-- the teardown-vs-delete distinction, stated once as a SIBLING of the
|
|
1477
1477
|
volatile slot (the #mRepoVisWhy shape) so no re-render can drop it.
|
|
1478
1478
|
Nothing on this page erases a project, and the copy says so rather
|
|
@@ -1488,7 +1488,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1488
1488
|
#mDelWhat, so no re-render can drop what it promises. -->
|
|
1489
1489
|
<div class="card mSec" id="mDisconnect">
|
|
1490
1490
|
<h2>Disconnect from Bongos entirely</h2>
|
|
1491
|
-
<div id="mDisc"><p class="finePrint">Reading the project…</p></div>
|
|
1491
|
+
<div id="mDisc"><p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p></div>
|
|
1492
1492
|
<details class="finePrint" id="mDiscWhat">
|
|
1493
1493
|
<summary>What this erases — and what it keeps</summary>
|
|
1494
1494
|
<p><b>Erased for good:</b> the project’s server, its database and every nightly backup of it, its settings, its place on the map and the list of its members. Its name and web address are released, so another project can take them.</p>
|
|
@@ -1767,7 +1767,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1767
1767
|
missing token is fetched by one automatic GitHub round-trip. Only when
|
|
1768
1768
|
that hop could not help does this line show, above the manual fields. -->
|
|
1769
1769
|
<div id="ghNoAccess" class="hint" hidden>We couldn’t read your GitHub repositories just now, so enter the repo by hand below.</div>
|
|
1770
|
-
<div id="ghBusy" class="hint" hidden>Reading your GitHub repositories…</div>
|
|
1770
|
+
<div id="ghBusy" class="hint loading-mark loading-mark--inline" role="status" hidden>Reading your GitHub repositories…</div>
|
|
1771
1771
|
<div id="ghAdopt" class="wizField" hidden>
|
|
1772
1772
|
<span>Pick one of your repositories</span>
|
|
1773
1773
|
<input type="text" id="ghSearch" placeholder="Search your repos…" autocomplete="off" aria-label="Search your repositories">
|
|
@@ -9836,7 +9836,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
9836
9836
|
repoVisMsg('<p class="finePrint">No repository is linked to this project.</p>');
|
|
9837
9837
|
return;
|
|
9838
9838
|
}
|
|
9839
|
-
repoVisMsg('<p class="finePrint">Checking the repository on GitHub…</p>');
|
|
9839
|
+
repoVisMsg('<p class="finePrint loading-mark loading-mark--inline" role="status">Checking the repository on GitHub…</p>');
|
|
9840
9840
|
var asked = manageId; /* a stale answer must never describe a newer project */
|
|
9841
9841
|
fetch(API + '/github/repo?repo=' + encodeURIComponent(inst.target_ref), { credentials: 'same-origin' })
|
|
9842
9842
|
.then(function (res) {
|
|
@@ -10169,7 +10169,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
10169
10169
|
function loadVisRuns(inst) {
|
|
10170
10170
|
if (!inst || inst.status !== 'active' || !addressReady(inst)) { visRuns(''); return; } /* the browser's own lookup waits for the name too (task 1004505) */
|
|
10171
10171
|
var asked = manageId;
|
|
10172
|
-
visRuns('Checking what the project is running
|
|
10172
|
+
visRuns('<span class="loading-mark loading-mark--inline" role="status">Checking what the project is running…</span>');
|
|
10173
10173
|
ensureManifest(inst.domain).then(function (ans) {
|
|
10174
10174
|
if (asked !== manageId) return;
|
|
10175
10175
|
if (!ans.ok) {
|
|
@@ -10232,7 +10232,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
10232
10232
|
|
|
10233
10233
|
function loadVisibility(inst) {
|
|
10234
10234
|
mVisSettings = null; mVisRunsHtml = ''; mVisPushSeen = false;
|
|
10235
|
-
visMsg('<p class="finePrint">Reading the project’s settings…</p>');
|
|
10235
|
+
visMsg('<p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project’s settings…</p>');
|
|
10236
10236
|
var asked = manageId; /* a stale answer must never describe a newer project */
|
|
10237
10237
|
ensureSettings()
|
|
10238
10238
|
.then(function (ans) {
|
|
@@ -10348,7 +10348,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
10348
10348
|
$('mLookActions').hidden = true;
|
|
10349
10349
|
$('mLookMsg').textContent = ''; $('mLookMsg').className = 'projMsg';
|
|
10350
10350
|
while (host.firstChild) host.removeChild(host.firstChild);
|
|
10351
|
-
var wait = document.createElement('p'); wait.className = 'finePrint'; wait.textContent = 'Reading the project…';
|
|
10351
|
+
var wait = document.createElement('p'); wait.className = 'finePrint loading-mark loading-mark--inline'; wait.setAttribute('role', 'status'); wait.textContent = 'Reading the project…';
|
|
10352
10352
|
host.appendChild(wait);
|
|
10353
10353
|
var forId = inst.id;
|
|
10354
10354
|
fetch(API + '/provisioning/instances/' + encodeURIComponent(inst.id) + '/look', { credentials: 'same-origin' })
|
|
@@ -10622,7 +10622,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
10622
10622
|
function loadJoinRuns(inst) {
|
|
10623
10623
|
if (!inst || inst.status !== 'active' || !addressReady(inst)) { joinRuns(''); return; } /* the browser's own lookup waits for the name too (task 1004505) */
|
|
10624
10624
|
var asked = manageId;
|
|
10625
|
-
joinRuns('Checking what the project is running
|
|
10625
|
+
joinRuns('<span class="loading-mark loading-mark--inline" role="status">Checking what the project is running…</span>');
|
|
10626
10626
|
ensureManifest(inst.domain).then(function (ans) {
|
|
10627
10627
|
if (asked !== manageId) return;
|
|
10628
10628
|
if (!ans.ok) {
|
|
@@ -10648,7 +10648,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
10648
10648
|
|
|
10649
10649
|
function loadJoinability(inst) {
|
|
10650
10650
|
mJoinSettings = null; mJoinRunsHtml = ''; mJoinPushSeen = false;
|
|
10651
|
-
joinMsg('<p class="finePrint">Reading the project’s settings…</p>');
|
|
10651
|
+
joinMsg('<p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project’s settings…</p>');
|
|
10652
10652
|
var asked = manageId; /* a stale answer must never describe a newer project */
|
|
10653
10653
|
ensureSettings()
|
|
10654
10654
|
.then(function (ans) {
|
|
@@ -10854,7 +10854,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
10854
10854
|
gets the clause, so a slow network never reports a break that isn't */
|
|
10855
10855
|
modsSlot(modsFailed
|
|
10856
10856
|
? '<p class="finePrint">We couldn’t load the list of extras just now — refresh in a moment. Nothing about your project has changed.</p>'
|
|
10857
|
-
: '<p class="finePrint">Reading the project…</p>');
|
|
10857
|
+
: '<p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project…</p>');
|
|
10858
10858
|
return;
|
|
10859
10859
|
}
|
|
10860
10860
|
var on = modsSelected(inst);
|
|
@@ -10957,7 +10957,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
10957
10957
|
|
|
10958
10958
|
function loadDoor() {
|
|
10959
10959
|
mDoorSettings = null;
|
|
10960
|
-
doorMsg('<p class="finePrint">Reading the project’s settings…</p>');
|
|
10960
|
+
doorMsg('<p class="finePrint loading-mark loading-mark--inline" role="status">Reading the project’s settings…</p>');
|
|
10961
10961
|
var asked = manageId; /* a stale answer must never describe a newer project */
|
|
10962
10962
|
ensureSettings()
|
|
10963
10963
|
.then(function (ans) {
|
|
@@ -11177,7 +11177,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
11177
11177
|
if (mEnvSlug === inst.slug && mEnvVars && (mEnvAdding || mEnvEditing)) return;
|
|
11178
11178
|
if (mEnvSlug === inst.slug && mEnvVars) { renderEnvManifest(); return; }
|
|
11179
11179
|
mEnvSlug = inst.slug; mEnvVars = null; mEnvAdding = false; mEnvEditing = null;
|
|
11180
|
-
envMsg('<p class="finePrint">Reading the list…</p>');
|
|
11180
|
+
envMsg('<p class="finePrint loading-mark loading-mark--inline" role="status">Reading the list…</p>');
|
|
11181
11181
|
fetch(API + '/provisioning/instances/' + encodeURIComponent(inst.id) + '/env-manifest', { credentials: 'same-origin' })
|
|
11182
11182
|
.then(function (res) {
|
|
11183
11183
|
return jsonOrEmpty(res).then(function (data) {
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
</div>
|
|
208
208
|
</div>
|
|
209
209
|
<div id="versions" class="versions">
|
|
210
|
-
<p class="empty">The masons are at their tools…</p>
|
|
210
|
+
<p class="empty loading-mark" role="status">The masons are at their tools…</p>
|
|
211
211
|
</div>
|
|
212
212
|
</section>
|
|
213
213
|
|
|
@@ -307,7 +307,7 @@
|
|
|
307
307
|
</div>
|
|
308
308
|
</div>
|
|
309
309
|
<div id="world-snapshot" class="world-snapshot">
|
|
310
|
-
<p class="empty">The cartographers are still at their survey…</p>
|
|
310
|
+
<p class="empty loading-mark" role="status">The cartographers are still at their survey…</p>
|
|
311
311
|
</div>
|
|
312
312
|
<!-- #409: snapshot history collapsed behind a toggle. The latest snapshot
|
|
313
313
|
above is the prime view; the timeline of older snapshots is reference
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_": "The status page's states for modules/ui-design/kit/render.js (task 1005053, its first): the ledger once its reads have answered, from the stub's public fixtures. The status sheet has no dark rules, so `modes` names light alone and there is no query to walk. The stub ships no world snapshots, so the World tablet keeps its still line: its loader stopped when the snapshot list 404'd, which is the failure path this state pictures. The waiting state itself is looked at with the stub's LATENCY=<ms> (serve.js).",
|
|
3
|
+
"page": "/",
|
|
4
|
+
"surface": "status-ui",
|
|
5
|
+
"modes": ["light"],
|
|
6
|
+
"modeQuery": false,
|
|
7
|
+
"ignoreRequests": ["/snapshots/manifest\\.json$", "/api/(gds|bongos)/public/(grades|analytics/ecosystem)", "cdn\\.jsdelivr\\.net"],
|
|
8
|
+
"ignoreConsole": ["status of 404", "snapshots", "ecosystem", "Failed to load resource"],
|
|
9
|
+
"states": {
|
|
10
|
+
"loaded": { "auth": false, "actions": [["wait", 600]], "expect": { "visible": ["#versions", "#world-snapshot"], "count": [["#versions .loading-mark", 0], ["#world-snapshot .loading-mark", 0]] } }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -77,6 +77,13 @@ function clear(el) {
|
|
|
77
77
|
while (el.firstChild) el.removeChild(el.firstChild);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
// The loader (task 1005053) on a tablet whose read failed or found nothing:
|
|
81
|
+
// it stops turning and its words stay as they were, so a tablet never spins
|
|
82
|
+
// for ever. A later refresh that answers replaces the placeholder anyway.
|
|
83
|
+
function stopWaiting(el) {
|
|
84
|
+
if (el) for (const wait of el.querySelectorAll('.loading-mark')) wait.classList.remove('loading-mark');
|
|
85
|
+
}
|
|
86
|
+
|
|
80
87
|
async function fetchJson(path) {
|
|
81
88
|
const res = await fetch(`${API}${path}`, { cache: 'no-store' });
|
|
82
89
|
if (!res.ok) throw new Error(`${path} → ${res.status}`);
|
|
@@ -262,6 +269,7 @@ async function loadWorldSnapshots() {
|
|
|
262
269
|
if (!res.ok) throw new Error(`${res.status}`);
|
|
263
270
|
manifest = await res.json();
|
|
264
271
|
} catch (_) {
|
|
272
|
+
stopWaiting(els.worldSnapshot);
|
|
265
273
|
return;
|
|
266
274
|
}
|
|
267
275
|
|
|
@@ -276,7 +284,7 @@ async function loadWorldSnapshots() {
|
|
|
276
284
|
image: `${d}.png`,
|
|
277
285
|
}));
|
|
278
286
|
}
|
|
279
|
-
if (snapshots.length === 0) return;
|
|
287
|
+
if (snapshots.length === 0) { stopWaiting(els.worldSnapshot); return; }
|
|
280
288
|
|
|
281
289
|
// Oldest → newest already from server; reverse to put newest first in strip.
|
|
282
290
|
snapshots.sort((a, b) => (a.timestamp || '').localeCompare(b.timestamp || ''));
|
|
@@ -1114,7 +1122,7 @@ async function refresh() {
|
|
|
1114
1122
|
const tasks = [
|
|
1115
1123
|
loadHeadline().catch((e) => console.error('headline', e)),
|
|
1116
1124
|
loadWorldSnapshots().catch((e) => console.error('snapshots', e)),
|
|
1117
|
-
loadVersions().catch((e) => console.error('versions', e)),
|
|
1125
|
+
loadVersions().catch((e) => { stopWaiting(els.versions); console.error('versions', e); }),
|
|
1118
1126
|
loadCosts().catch((e) => console.error('costs', e)),
|
|
1119
1127
|
loadShipped().catch((e) => console.error('shipped', e)),
|
|
1120
1128
|
loadLeaderboard().catch((e) => console.error('leaderboard', e)),
|
|
@@ -320,6 +320,55 @@ main {
|
|
|
320
320
|
font-size: 1rem;
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
+
/* ---------- The loader (task 1005053; the hall's, task 1005043) ----------
|
|
324
|
+
The Bongos mark while a tablet waits for its data: the ring, the core and
|
|
325
|
+
the accent dot as two masks on the placeholder the tablet already had, whose
|
|
326
|
+
words stay as its caption. The ring and the dot turn together round the still
|
|
327
|
+
core, easing out of and back into the logo's pose each beat, and the ink
|
|
328
|
+
breathes. CSS only, on transform and filter only; every colour a token (the
|
|
329
|
+
black in the masks is alpha, not paint). It arrives after 240ms, so a read that
|
|
330
|
+
answers faster never shows it, and the Kill Switch at the foot of this sheet
|
|
331
|
+
leaves the still logo. A grid, so the dot shares the ring's cell whatever
|
|
332
|
+
padding the placeholder has. status.js takes the class off a placeholder whose
|
|
333
|
+
read failed or found nothing, so a tablet never spins for ever. */
|
|
334
|
+
.loading-mark {
|
|
335
|
+
--lm-size: 40px;
|
|
336
|
+
display: grid;
|
|
337
|
+
justify-items: center;
|
|
338
|
+
align-items: center;
|
|
339
|
+
gap: 12px;
|
|
340
|
+
padding: 28px 0;
|
|
341
|
+
text-align: center;
|
|
342
|
+
color: var(--ink-faint);
|
|
343
|
+
animation: loading-mark-arrive 500ms cubic-bezier(.16, 1, .3, 1) 240ms both;
|
|
344
|
+
}
|
|
345
|
+
.loading-mark::before,
|
|
346
|
+
.loading-mark::after {
|
|
347
|
+
content: "";
|
|
348
|
+
grid-area: 1 / 1;
|
|
349
|
+
width: var(--lm-size);
|
|
350
|
+
height: var(--lm-size);
|
|
351
|
+
-webkit-mask: var(--lm-shape) center / contain no-repeat;
|
|
352
|
+
mask: var(--lm-shape) center / contain no-repeat;
|
|
353
|
+
animation: loading-mark-turn 1.6s cubic-bezier(.45, .05, .55, .95) infinite;
|
|
354
|
+
}
|
|
355
|
+
/* the ring and the core: one ink, breathing */
|
|
356
|
+
.loading-mark::before {
|
|
357
|
+
--lm-shape: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cpath d='M137 31 A78 78 0 1 0 170 66' fill='none' stroke='black' stroke-width='15' stroke-linecap='round'/%3E%3Ccircle cx='100' cy='100' r='55'/%3E%3C/svg%3E");
|
|
358
|
+
background: color-mix(in srgb, var(--ink) 30%, var(--bg-card));
|
|
359
|
+
animation:
|
|
360
|
+
loading-mark-turn 1.6s cubic-bezier(.45, .05, .55, .95) infinite,
|
|
361
|
+
loading-mark-breathe .8s ease-in-out infinite alternate-reverse;
|
|
362
|
+
}
|
|
363
|
+
/* the dot: the accent, laid exactly over the mark and turning with it */
|
|
364
|
+
.loading-mark::after {
|
|
365
|
+
--lm-shape: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Ccircle cx='156' cy='46' r='12'/%3E%3C/svg%3E");
|
|
366
|
+
background: var(--accent);
|
|
367
|
+
}
|
|
368
|
+
@keyframes loading-mark-arrive { from { filter: opacity(0); transform: translateY(6px); } }
|
|
369
|
+
@keyframes loading-mark-turn { to { transform: rotate(1turn); } }
|
|
370
|
+
@keyframes loading-mark-breathe { from { filter: opacity(.45); } }
|
|
371
|
+
|
|
323
372
|
/* ---------- Versions / Works in Progress ---------- */
|
|
324
373
|
|
|
325
374
|
.versions {
|
|
@@ -1249,3 +1298,11 @@ main {
|
|
|
1249
1298
|
font-variant-numeric: tabular-nums;
|
|
1250
1299
|
line-height: 1;
|
|
1251
1300
|
}
|
|
1301
|
+
|
|
1302
|
+
/* ---------- The Kill Switch (task 1005053) ----------
|
|
1303
|
+
The platform floor (DESIGN.md, the Kill Switch Rule): under reduced motion
|
|
1304
|
+
nothing moves — the loader stands still as the logo, and every transition
|
|
1305
|
+
lands at once. Last in the sheet, so nothing after it can outrank it. */
|
|
1306
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1307
|
+
*, *::before, *::after { animation: none !important; transition: none !important; }
|
|
1308
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The stub, the renderer/auditor, the interaction probes, the mock checker and the contrast probe any builder points at any surface of any instance ([task 1003320](https://cloudbongos.com/builders#/task/1003320), [ADR 0197](../../../docs/adr/0197-the-ui-design-module.md)). Plain Node; the browser is the system Edge or Chrome via `playwright-core` (a devDependency — nothing is downloaded).
|
|
4
4
|
|
|
5
5
|
```
|
|
6
|
-
node modules/ui-design/kit/serve.js # the stub (SURFACE, PREFIX, AUTH=1, CHILD=1, LIVE=…, FAIL=…, STYLE=<look>, STUB_PORT)
|
|
6
|
+
node modules/ui-design/kit/serve.js # the stub (SURFACE, PREFIX, AUTH=1, CHILD=1, LIVE=…, FAIL=…, STYLE=<look>, LATENCY=<ms>, STUB_PORT)
|
|
7
7
|
node modules/ui-design/kit/render.js --page /projects # every state × 1440/390/320 × dark/light, audited against the floors (--style <look>)
|
|
8
8
|
node modules/ui-design/kit/probe.js --page /projects # the interaction contract as PASS/FAIL lines (--style <look>)
|
|
9
9
|
node modules/ui-design/kit/check-mock.js --file docs/design/mocks/hub/v2.html # (--pack <look> for a style's own mock)
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
// FAIL=<feed,feed> those feeds answer 500 (the names in FEEDS below)
|
|
24
24
|
// STYLE=<name | pack.json> serve under a look from the style library (modules/ui-design/
|
|
25
25
|
// styles/<name>/pack.json laid over the instance's pack; task 1003323)
|
|
26
|
+
// LATENCY=<ms> hold every /api/ read that long before answering, so a page's
|
|
27
|
+
// waiting state (the loader, task 1005053) can be looked at
|
|
26
28
|
//
|
|
27
29
|
// Exit 2 on a surface that does not resolve. Prints ONE line when listening
|
|
28
30
|
// ("stub on http://…") — render.js and probe.js wait for it.
|
|
@@ -43,6 +45,7 @@ const CHILD = process.env.CHILD === '1';
|
|
|
43
45
|
const LIVE = process.env.LIVE || '';
|
|
44
46
|
const FAIL = new Set(String(process.env.FAIL || '').split(',').map((s) => s.trim()).filter(Boolean));
|
|
45
47
|
const STYLE = process.env.STYLE || '';
|
|
48
|
+
const LATENCY = Math.max(0, Number(process.env.LATENCY) || 0);
|
|
46
49
|
// a states file may point a fixture NAME at another FILE (stub.fixtures, task 1003345) — the
|
|
47
50
|
// way the mine view's empty state is rendered without a second stub shape
|
|
48
51
|
const ALIAS = JSON.parse(process.env.FIXTURE_ALIAS || '{}');
|
|
@@ -792,6 +795,10 @@ function scoutingRead(res, origin) {
|
|
|
792
795
|
}
|
|
793
796
|
|
|
794
797
|
const server = http.createServer((req, res) => {
|
|
798
|
+
if (LATENCY && /^\/api\//.test(req.url)) return void setTimeout(() => answer(req, res), LATENCY);
|
|
799
|
+
return answer(req, res);
|
|
800
|
+
});
|
|
801
|
+
function answer(req, res) {
|
|
795
802
|
const u = new URL(req.url, 'http://x');
|
|
796
803
|
const p = u.pathname;
|
|
797
804
|
const origin = `http://127.0.0.1:${server.address().port}`;
|
|
@@ -965,7 +972,7 @@ const server = http.createServer((req, res) => {
|
|
|
965
972
|
catch (e) { res.writeHead(500, { 'content-type': 'text/plain' }); return res.end('render failed: ' + e.stack); }
|
|
966
973
|
}
|
|
967
974
|
res.writeHead(404, { 'content-type': 'text/plain' }); res.end('404 ' + p);
|
|
968
|
-
}
|
|
975
|
+
}
|
|
969
976
|
server.listen(PORT, '127.0.0.1', () => {
|
|
970
977
|
const port = server.address().port;
|
|
971
978
|
process.stdout.write(`stub on http://127.0.0.1:${port}${PREFIX} surface=${path.relative(ROOT, DIR) || '.'} auth=${AUTH} child=${CHILD} live=${LIVE || '-'} fail=${[...FAIL].join(',') || '-'} style=${style ? style.name : '-'}\n`);
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.27",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.24.
|
|
9
|
+
"version": "1.24.27",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.27",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -11081,5 +11081,19 @@
|
|
|
11081
11081
|
"id": "1004736",
|
|
11082
11082
|
"text": "A hosted project's Update button no longer leads to a missing page; it opens the Upgrade page for that project."
|
|
11083
11083
|
}
|
|
11084
|
+
],
|
|
11085
|
+
"1.24.27": [
|
|
11086
|
+
{
|
|
11087
|
+
"id": "1005041",
|
|
11088
|
+
"text": "Updating Bongos from a project's hall is now one button: a small Update pill in the top bar updates the project right there, shows progress, and says Reload when it is done."
|
|
11089
|
+
},
|
|
11090
|
+
{
|
|
11091
|
+
"id": "1004546",
|
|
11092
|
+
"text": "The unattended AI builder can no longer change its own safety rules or off switch: it skips that work, refuses to hand it in, and the server turns it away if it tries."
|
|
11093
|
+
},
|
|
11094
|
+
{
|
|
11095
|
+
"id": "1005053",
|
|
11096
|
+
"text": "The account, projects, community and status pages now show the Bongos logo turning while they wait for data, instead of a plain \"Reading…\" line."
|
|
11097
|
+
}
|
|
11084
11098
|
]
|
|
11085
11099
|
}
|
|
@@ -101,6 +101,10 @@ function buildWorkerPrompt({ task, packPath = 'docs/packs/engineer.md' } = {}) {
|
|
|
101
101
|
'',
|
|
102
102
|
'If the "feature" itself is to weaken a gate, change permissions, or touch the deploy or methodology core, STOP and return cannot_complete: those are human-only.',
|
|
103
103
|
'',
|
|
104
|
+
// task 1004546: the server refuses a runner pull request that changes a protected
|
|
105
|
+
// file, and a session-log file makes the ship regenerate the protected CLAUDE.md.
|
|
106
|
+
'Do not add a session-log file under docs/session-logs/: the run log and your ship notes are the record, and the server refuses a runner change that touches a protected file such as CLAUDE.md. If the work needs a protected file or one of the runner\'s own files, ship.js will refuse; return cannot_complete and name the files.',
|
|
107
|
+
'',
|
|
104
108
|
'Ship it yourself when the work is done and verified: `node scripts/bongos/ship.js <id> --no-surprise --notes \"...\" --summary \"...\"`. Do NOT pass --skip-grade and do not force any gate; if the grader or the merge refuses, that is the system protecting main — return cannot_complete and say so.',
|
|
105
109
|
'',
|
|
106
110
|
// task 1004536: stated here so the worker does not waste a session trying —
|
|
@@ -51,6 +51,9 @@ const tweakLane = require('./autobongos-tweak-lane.js');
|
|
|
51
51
|
// where it would not be inside modules/ — which is exactly why fence.js takes the
|
|
52
52
|
// hits as an argument instead of matching them itself.
|
|
53
53
|
const { matchProtected } = require('../../src/bongos/permission-path-check');
|
|
54
|
+
// The runner's OWN files (task 1004546): one list, read here before the claim,
|
|
55
|
+
// by ship.js inside the worker, and by the server at publish.
|
|
56
|
+
const runnerSelf = require('../../modules/lifecycle/runner-self.js');
|
|
54
57
|
const { resolveEnv, envPrefix, LEGACY_ENV_PREFIXES } = require('../../src/instance-config');
|
|
55
58
|
const cadence = require('../../modules/autonomy/cadence.js');
|
|
56
59
|
const configIdle = require('../../modules/autonomy/config-idle.js');
|
|
@@ -215,8 +218,17 @@ async function pickTask(goals, deps = {}) {
|
|
|
215
218
|
// reason. It is decided here and not in sequence.js's matrix because the
|
|
216
219
|
// matrix also serves /builder-sequence, where the builder may well hold the
|
|
217
220
|
// host access an unattended runner never has.
|
|
221
|
+
//
|
|
222
|
+
// So is work on the runner's own files (task 1004546), for the same reason and
|
|
223
|
+
// one more: decideTask below refuses it too, but a refusal there returns from
|
|
224
|
+
// the iteration, and the picker would hand back the same task next time.
|
|
218
225
|
const hostSkipped = [];
|
|
219
226
|
const open = unexcluded.filter((t) => {
|
|
227
|
+
const self = runnerSelf.matchRunnerSelf(t.touches || []);
|
|
228
|
+
if (self.length) {
|
|
229
|
+
hostSkipped.push({ id: t.id, title: t.title, reasons: [{ code: 'self_modification', detail: `${fence.REFUSALS.self_modification} (${self.map((h) => h.file).join(', ')})` }] });
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
220
232
|
const host = fence.needsHostAccess(t);
|
|
221
233
|
if (!host.needs) return true;
|
|
222
234
|
hostSkipped.push({ id: t.id, title: t.title, reasons: [{ code: 'needs_host_access', detail: `${fence.REFUSALS.needs_host_access} (${host.signals.join('; ')})` }] });
|
|
@@ -275,7 +287,9 @@ function workerEnv(source = process.env, extra = {}) {
|
|
|
275
287
|
// An operator can widen this deliberately and visibly, never by accident.
|
|
276
288
|
const widen = String(source.AUTOBONGOS_WORKER_ENV_EXTRA || '').split(',').map((x) => x.trim()).filter(Boolean);
|
|
277
289
|
for (const k of widen) if (source[k] !== undefined) out[k] = source[k];
|
|
278
|
-
|
|
290
|
+
// AUTOBONGOS_WORKER tells ship.js it runs unattended, so it refuses a diff on
|
|
291
|
+
// the runner's own files before spending a grade (task 1004546).
|
|
292
|
+
return { ...out, OTB_SUBAGENT: '1', [runnerSelf.RUNNER_WORKER_ENV]: '1', ...extra };
|
|
279
293
|
}
|
|
280
294
|
|
|
281
295
|
const MAX_WORKER_OUTPUT = 8 * 1024 * 1024; // a misbehaving worker must not grow memory until the timeout
|
|
@@ -642,9 +656,10 @@ async function iteration(opts, deps = {}) {
|
|
|
642
656
|
// OUTER of three layers and would not be as the only one.
|
|
643
657
|
const taskGate = fence.decideTask({
|
|
644
658
|
task: candidate, goals: gate.goals, protectedHits: matchProtected(candidate.touches || []),
|
|
659
|
+
selfHits: runnerSelf.matchRunnerSelf(candidate.touches || []),
|
|
645
660
|
});
|
|
646
661
|
if (!taskGate.go) {
|
|
647
|
-
return log({ event: 'fenced', code: taskGate.code, reason: taskGate.reason, task_id: candidate.id, goal_id: got.goalId, ...pickedFrom(got.goalId, gate), globs: taskGate.globs || [] });
|
|
662
|
+
return log({ event: 'fenced', code: taskGate.code, reason: taskGate.reason, task_id: candidate.id, goal_id: got.goalId, ...pickedFrom(got.goalId, gate), globs: taskGate.globs || [], ...(taskGate.files ? { files: taskGate.files } : {}) });
|
|
648
663
|
}
|
|
649
664
|
|
|
650
665
|
if (opts.dryRun) return log({ event: 'dry_run', task_id: candidate.id, title: candidate.title, goal_id: got.goalId, ...pickedFrom(got.goalId, gate) });
|
|
@@ -786,9 +801,11 @@ async function iteration(opts, deps = {}) {
|
|
|
786
801
|
//
|
|
787
802
|
// Derived FROM worktreeName so the two cannot drift: change the name format and
|
|
788
803
|
// this regex is what stops the next reconciliation from quietly matching nothing.
|
|
804
|
+
// It lives in modules/lifecycle/runner-self.js (task 1004546), because the server
|
|
805
|
+
// reads the same shape to tell a runner's claim at publish, and three copies of one
|
|
806
|
+
// regex was a grade finding on task 1004540.
|
|
789
807
|
const CLAIM_PREFIX = 'autobongos-';
|
|
790
|
-
const RUNNER_TREE_RE =
|
|
791
|
-
const isRunnerTree = (name) => RUNNER_TREE_RE.test(String(name || ''));
|
|
808
|
+
const { RUNNER_TREE_RE, isRunnerTree } = runnerSelf;
|
|
792
809
|
|
|
793
810
|
// ── the heartbeat, and why reconciliation needs one ──────────────────────────
|
|
794
811
|
//
|
|
@@ -34,9 +34,10 @@ const LANE_RANK_FLOOR = 'metic';
|
|
|
34
34
|
// D6.2, the same number the server refuses a third marked claim at.
|
|
35
35
|
const MAX_AUTO_APPLIES_PER_ROUND = 2;
|
|
36
36
|
const MAX_ROUNDS_READ_PER_PASS = 10;
|
|
37
|
-
// The runner's own tree names (autobongos-run.js worktreeName).
|
|
38
|
-
//
|
|
39
|
-
|
|
37
|
+
// The runner's own tree names (autobongos-run.js worktreeName). Read from the one
|
|
38
|
+
// spelling in modules/lifecycle/runner-self.js, which requires nothing, so there is
|
|
39
|
+
// no cycle back to the runner (task 1004546).
|
|
40
|
+
const { RUNNER_TREE_RE, matchRunnerSelf } = require('../../modules/lifecycle/runner-self.js');
|
|
40
41
|
// The fence refusals that are about the goal allowlist and nothing else: the lane
|
|
41
42
|
// runs past them (D3). The kill switch, a paused builder and the grader stop do not.
|
|
42
43
|
const GOAL_ONLY_CODES = new Set(['goal_not_allowlisted', 'no_allowlist', 'no_builder_scope']);
|
|
@@ -92,6 +93,7 @@ function decideRound({ task, claims, files, failedAt, matchProtected }) {
|
|
|
92
93
|
task: { ...task, touches: files },
|
|
93
94
|
goals: task.goal_id == null ? [] : [task.goal_id],
|
|
94
95
|
protectedHits: matchProtected(files),
|
|
96
|
+
selfHits: matchRunnerSelf(files), // a page's files are never the runner's own, but the gate asks anyway (task 1004546)
|
|
95
97
|
});
|
|
96
98
|
}
|
|
97
99
|
|
|
@@ -38,6 +38,24 @@ const { noteServerReward } = require('./ship-state.js');
|
|
|
38
38
|
const { recordGradeOutcome } = require('./autobongos-grade-cap.js');
|
|
39
39
|
// task 1004631: on a Manual project an AI session asks before it ships (ADR 0369).
|
|
40
40
|
const manualShip = require('./ship-mode-manual.js');
|
|
41
|
+
// task 1004546: an Autobongos worker never ships a change to the runner's own files.
|
|
42
|
+
const runnerSelf = require('../../modules/lifecycle/runner-self.js');
|
|
43
|
+
const { matchProtected } = require('../../src/bongos/permission-path-check');
|
|
44
|
+
|
|
45
|
+
// refuseRunnerSelfEdit(files) — inside an Autobongos worker (the supervisor sets
|
|
46
|
+
// AUTOBONGOS_WORKER), refuse a committed diff that touches the runner's own
|
|
47
|
+
// supervisor, safety checks or kill switch, or an ADR 0043 protected file (task
|
|
48
|
+
// 1004546). It runs on the real diff before the grade, so a refused task spends no
|
|
49
|
+
// grade and keeps its claim for the supervisor to release. The server refuses the
|
|
50
|
+
// same diff at publish, so a worker that unsets the variable gains nothing.
|
|
51
|
+
// Outside a worker it does nothing.
|
|
52
|
+
function refuseRunnerSelfEdit(files) {
|
|
53
|
+
const refusal = runnerSelf.selfEditRefusal({ files, matchProtected });
|
|
54
|
+
if (!refusal) return;
|
|
55
|
+
console.error(`Ship refused (${refusal.code}): ${runnerSelf.selfEditMessage(refusal)}`);
|
|
56
|
+
console.error('Do not work around this. Return cannot_complete and name the files; your claim is still active.');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
41
59
|
|
|
42
60
|
// stopForManualApproval(args, taskId) — Manual ship mode (task 1004631, ADR 0369). Every path
|
|
43
61
|
// that can LAND work calls this first: a fresh ship, a resumed merge, and a re-grade (whose pass
|
|
@@ -303,6 +321,7 @@ async function shipMain() {
|
|
|
303
321
|
// claim ACTIVE so the builder can commit + re-run.
|
|
304
322
|
const graderBaseline = computeGraderBaseline(ac);
|
|
305
323
|
const committedFiles = computeChangedFiles(graderBaseline);
|
|
324
|
+
refuseRunnerSelfEdit(committedFiles);
|
|
306
325
|
if (committedFiles.length === 0) {
|
|
307
326
|
const verdict = classifyEmptyShip(gitOk('git status --porcelain'), { allowEmpty });
|
|
308
327
|
if (verdict.reason === 'uncommitted-work') {
|
|
@@ -1020,6 +1039,7 @@ async function regradeMain(opts = {}) {
|
|
|
1020
1039
|
// ---- Empty-diff guard (the grader reads committed history) ----
|
|
1021
1040
|
const graderBaseline = computeGraderBaseline(claimLike);
|
|
1022
1041
|
const committedFiles = computeChangedFiles(graderBaseline);
|
|
1042
|
+
refuseRunnerSelfEdit(committedFiles);
|
|
1023
1043
|
if (committedFiles.length === 0) {
|
|
1024
1044
|
// task 1003147: a DELIBERATELY artifact-free task used to dead-end here.
|
|
1025
1045
|
// shipMain routes every 'completed' task to this re-grade BEFORE reading any
|