Row 286: library.status shows the credential (webby-0151 R3); fired chief revokes its row as row 281 does; unrun

This commit is contained in:
jason.woltje committed 2026-10-24 08:35:03 -05:00
1 parent 4b97ce7b2a
commit 8fdd496e49
2 files changed
+24 -7

No files matched your search

+6 -2
View File
@@ -193,8 +193,9 @@ Choices made without a ruling. Each is small and reversible:
source runtime's `slotDir` uses.
- `BusError` carries only a code, so the sage-2928 fix text can't ride on the verb's refusal. It
goes in the timer's log line (`fix`) and in the README. The verb answers `library-fetch-no-grant`.
- T7 stages revoke and fire as the state those verbs leave (row deleted plus `grant.revoked`; name
delisted with `roles.chief` kept, and a `fire.stopped` with no `fire.done`). The chief has no Gitea
- T7 stages revoke and fire as the state those verbs leave: row deleted plus `grant.revoked`; for a
fire, the name delisted with `roles.chief` kept and the row revoked (row 281's fire revokes grants).
A delisted chief with its row still there, and a `fire.stopped` with no `fire.done`, also refuse. The chief has no Gitea
grant path before C2-21, so the evidence and the row go in by SQL, as gitea-grant.test.mjs does for
decisions. The verbs' own effects are pinned in gitea-grant.test.mjs and fire-revokes-grants.test.mjs.
This departs from the brief's "through grants.revoke" and "through instance.fire". For Dewey.
@@ -210,6 +211,9 @@ Test map:
unsafe credential runs no git, T10, fetch credential and state with N1/T12, T5+T5b.
- bus library-credential.test.mjs: T6, T7, T8, departure, T9 runtime (chief file and user via timer
and verb; revoked with slot 0000: no git, log `fix`, state `credential: null`).
- T9 at runtime covers the revoked case with the slot at 0000. Fired and held are unit-level in the
broker; `sync` with any `{ refused }` opens no file and runs no git (library test, valid slot present).
- webby-0151 R3: T9 reads `library.status` for `credential` and `lastError`.
- T11 token search: T1 scans the data root and every file but the seat's; T9 scans the log lines.
The gate's output search uses the fixture tokens `fixture-token-*-made-up-00{42..49}`.
+18 -5
View File
@@ -81,16 +81,22 @@ test('C2-03b T7: a revoked, fired or held chief each refuse library-fetch-no-gra
revoke(revoked.store, 'chief');
assert.deepEqual(revoked.choose(), REFUSED, 'revoked');
// A fire delists the name; roles[chief] stays, so its evidence still ends the fallback.
// A fire delists the name and, since row 281, revokes its grants; roles[chief] stays, so its
// evidence still ends the fallback.
const fired = world(t, { instances: ['coder'] });
grant(fired.store, 'chief');
revoke(fired.store, 'chief');
assert.ok(fired.business.roles.chief, 'roles[chief] remains after a fire');
assert.deepEqual(fired.choose(), REFUSED, 'fired');
fired.business.launch.instances.push('chief');
assert.deepEqual(fired.choose(), { kind: 'chief', agent: 'chief', user: 'chief-bot' }, 'a rehire lists it again');
fired.store.run("INSERT INTO events(id,at,business,kind,actor_role,actor_run,subject,body) VALUES('f1',?,?,'action.allowed','chief','run','chief',?)",
// The listed check alone: delisted with the row still there, then a fire under way.
const delisted = world(t, { instances: ['coder'] });
grant(delisted.store, 'chief');
assert.deepEqual(delisted.choose(), REFUSED, 'delisted');
delisted.business.launch.instances.push('chief');
assert.deepEqual(delisted.choose(), { kind: 'chief', agent: 'chief', user: 'chief-bot' }, 'a rehire lists it again');
delisted.store.run("INSERT INTO events(id,at,business,kind,actor_role,actor_run,subject,body) VALUES('f1',?,?,'action.allowed','chief','run','chief',?)",
AT, 'acme', JSON.stringify({ action: 'fire.stopped' }));
assert.deepEqual(fired.choose(), REFUSED, 'a fire under way');
assert.deepEqual(delisted.choose(), REFUSED, 'a fire under way');
const held = world(t);
grant(held.store, 'chief');
@@ -145,6 +151,10 @@ test('C2-03b T9 runtime: the chief\'s store file and user, and a refusal that ru
await until(() => calls.length >= 1);
const human = runtime.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true });
await runtime.broker.request(human, { verb: 'library.sync', args: { project: 'books' } }).catch(e => e);
// webby-0151 R3: library.status names the kind and user of the last fetch, never the file or value.
const shown = await runtime.broker.request(human, { verb: 'library.status', args: { project: 'books' } });
assert.deepEqual(shown.credential, { kind: 'chief', user: 'chief-bot' });
assert.equal(JSON.stringify(shown).includes(chiefFile) || JSON.stringify(shown).includes(CHIEF_TOKEN), false);
await runtime.close();
assert.ok(calls.length >= 2, 'the timer and the verb');
assert.ok(calls.every(c => c.kind === 'fetch' && c.opts.slot === chiefFile && c.opts.user === 'chief-bot'), 'the chief\'s file and user, never the slot');
@@ -159,6 +169,9 @@ test('C2-03b T9 runtime: the chief\'s store file and user, and a refusal that ru
await until(() => lines.length >= 1);
const human2 = runtime.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true });
await assert.rejects(runtime.broker.request(human2, { verb: 'library.sync', args: { project: 'books' } }), { code: 'library-fetch-no-grant' });
const after = await runtime.broker.request(human2, { verb: 'library.status', args: { project: 'books' } });
assert.equal(after.lastError.code, 'library-fetch-no-grant');
assert.equal(after.credential, null);
await runtime.close();
assert.equal(calls.length, 0, 'no git with a refusal');
assert.equal(statSync(slot).mode & 0o777, 0, 'the slot was never fixed up');