bus: row 236, grants.status is a read under the kill switch, as hosts.status is (sage-2453, #1742)
Row 191's #processingGate refuses agent verbs off its read list while processing is halted. grants.status joins hosts.status on that list, per the kill-switch plan (2026-10-20_kill-switch.md:16): reads still work. An agent's grants.revoke stays refused while halted, as hosts.revoke is.
This commit is contained in:
1 parent
c8e90f5f03
commit
08840f3c15
3 files changed
+27
-1
No files matched your search
@@ -12,6 +12,10 @@ runs on Sage's gate.
|
||||
- Build carry: `agents/megavolt/work/436b/BUILD-CARRY.md` at 3330c901b (N9, N10).
|
||||
- Schema label 3j-3k per sage-2237. If 264a or 206 lands first, this rebases onto a new branch name and reruns
|
||||
the whole bus suite, the schema.test counts and the T46 lists.
|
||||
- Rebases: onto 86f95bb27 after row 273 (sage-2403, no conflicts), then onto 0fa7bf421 after row 191
|
||||
(sage-2452, sage-2457). The 191 rebase took three unions in commit 1, 191's `processing` first and then the
|
||||
grant options: the Broker constructor (broker.mjs:317), the runtime's Broker options (runtime.mjs:173) and
|
||||
the cli case line in `scripts/mosaic` (191's `halt|resume|processing|` plus `grants`).
|
||||
|
||||
## What changed
|
||||
|
||||
@@ -78,6 +82,11 @@ Each is a reading of the design where it left room. Dewey may rule otherwise.
|
||||
revoke check. A revoke during the whoami ends the grant `revoked`, not `grant-lost`, and the
|
||||
token-unknown branch neither holds the agent again nor writes `grant-token-unknown`, so no start
|
||||
names a file the revoke unlinked.
|
||||
21. sage-2453, ruling (a) on megavolt-0123: 191's `#processingGate` refuses every agent verb off its read list
|
||||
while processing is halted. `grants.status` is a read like `hosts.status`, and the kill-switch plan
|
||||
(`docs/plans/2026-10-20_kill-switch.md`:16) says "Reads and the human's own verbs, including the clear,
|
||||
still work", so it joins the list. This touches row 191's list (one entry). An agent's `grants.revoke`
|
||||
stays refused while halted, as `hosts.revoke` does.
|
||||
|
||||
## Named for DEFERRED (r2, Dewey N1-N4)
|
||||
|
||||
@@ -135,3 +144,6 @@ r2 tests, each red on 8bf442be9 by reading:
|
||||
- N5 (r3, sage-2388): "X-G11: a revoke during a settle whoami that finds a foreign token ends it revoked, not
|
||||
held" and "X-G11: a revoke during a settle whoami that gets a 401 ends it revoked, not lost". On 77dd43c9e
|
||||
they end `grant-token-unknown` (the agent back in tokenUnknown) and `grant-lost`.
|
||||
- Choice 21 (sage-2453): "kill switch (plan :16): while halted the chief still reads grants.status, and an
|
||||
agent grants.revoke refuses". M38 drops `'grants.status'` from the read list; the chief's status then
|
||||
refuses `processing-halted` and the test fails.
|
||||
@@ -419,7 +419,7 @@ export class Broker {
|
||||
if (s.human || s.reader || s.tester) return;
|
||||
if (!['inbox', 'agents', 'tasks', 'trail', 'decisions', 'replies', 'messages', 'state', 'usage',
|
||||
'requirements', 'spec.read', 'project.list', 'project.files', 'project.file', 'library.file', 'library.status',
|
||||
'thread.list', 'thread.messages', 'names', 'instances', 'routes', 'hosts.status',
|
||||
'thread.list', 'thread.messages', 'names', 'instances', 'routes', 'hosts.status', 'grants.status',
|
||||
'processing.status', 'role.release', 'usage.end'].includes(verb)) this.#processingWork();
|
||||
}
|
||||
reconcileProcessing() { this.#processingMirror(this.#processingState()); }
|
||||
|
||||
@@ -88,6 +88,7 @@ async function setup(t, opts = {}) {
|
||||
secretCheck: (value) => credentials.assertClean(value),
|
||||
giteaGrant: grantApi,
|
||||
giteaKeys: keys,
|
||||
processing: opts.processing,
|
||||
credentialVerbs: (id, verb, args, actor) => {
|
||||
calls.push([verb, actor]);
|
||||
if (verb === 'credentials.revoke' || verb === 'credentials.delete') {
|
||||
@@ -895,6 +896,19 @@ test('X-G11: run refuses every caller but the human, an unapproved, a spent and
|
||||
assert.deepEqual(f.grantEvents().filter((e) => 'decision' in e).map((e) => e.operation), ['grant.gitea']);
|
||||
});
|
||||
|
||||
test('kill switch (plan :16): while halted the chief still reads grants.status, and an agent grants.revoke refuses', async (t) => {
|
||||
const control = { halted: false, version: 1, generation: randomUUID() };
|
||||
const f = await setup(t, { processing: { read: () => ({ ...control }) } });
|
||||
await f.approve();
|
||||
control.halted = true;
|
||||
const status = await f.call('human', 'grants.status', { service: 'gitea' });
|
||||
assert.deepEqual(await f.call('chief', 'grants.status', { service: 'gitea' }), status);
|
||||
// The gate throws before dispatch, as in processing.test.mjs, so the call is wrapped.
|
||||
await rejects(async () => f.call('pm', 'grants.revoke', { service: 'gitea', agent: 'coder', reason: 'r' }), 'processing-halted');
|
||||
assert.equal(f.record().agent, 'coder');
|
||||
assert.equal(f.state(), 'valid');
|
||||
});
|
||||
|
||||
test('X-G11: a revoke while the executor runs aborts it and the grant ends revoked', async (t) => {
|
||||
const f = await setup(t);
|
||||
let release;
|
||||
|
||||
Reference in new issue
Block a user