honker: row 194 design r3 (sage-1374, sage-1377: Q4 hireable stays empty, instance.name on the hire floor, fire ends runs F4) and PRD 1.7 draft REQ-LAND-2 git.push.remote for coder

This commit is contained in:
jason.woltje committed 2026-10-20 14:31:19 -05:00
1 parent c0a35f47a0
commit d0afda0243
2 files changed
+90 -23

No files matched your search

+48 -10
View File
@@ -256,6 +256,23 @@ Fire refuses `instance-kept` for a kept instance, `organization-definition-refus
fired instance stays in `roles`. The chief never deletes. Delete stays a human act because it retires the fired instance stays in `roles`. The chief never deletes. Delete stays a human act because it retires the
name. name.
Fire ends the fired instance's processes, not only its claims (F4, Megavolt through sage-1374). Every run
gets the shared model token at start: `ENV_ALLOW` (`packages/seat/src/session.mjs:83-94`) passes
`ANTHROPIC_API_KEY`, `CLAUDE_CODE_OAUTH_TOKEN` and the other provider variables into the sandbox. A revoke
can't take that token back, so a fired agent whose process lives on still holds it. Fire therefore runs V3
steps 1 to 3 in the same transaction as the delist:
1. It revokes every claim the instance holds.
2. It writes `lifecycle.cancelled` for each of the instance's open requests.
3. It writes `lifecycle.end` with the runs. The launcher pass sends SIGTERM to each runner after the
start-time check (`launcher.mjs:526`), spawned but unbound registry runs included.
Fire's answer lists the runs it signalled. The delist stops any new chief launch (`launch` takes listed
instances only). A human launch request already taken is past cancelling, as in V3. The human made it, the
fire answer names it, and the human's terminate ends its run. Ten seconds after the signal, the launcher pass checks
each signalled runner again. A runner still alive gets `lifecycle.end.alive` with the run and pid, and the
Console's Agents section shows it to the human, who uses `mosaic stop`. There is still no SIGKILL (N6, R7).
### D2.3 The hire floor (B2, sage-1365 item 4) ### D2.3 The hire floor (B2, sage-1365 item 4)
A definition the chief can never hire, whatever `hireable` says. It fails the floor when its withinRole or A definition the chief can never hire, whatever `hireable` says. It fails the floor when its withinRole or
@@ -266,10 +283,11 @@ crossRole holds any of:
| `role.launch` | launch authority | | `role.launch` | launch authority |
| `role.end`, `role.revoke` | end authority | | `role.end`, `role.revoke` | end authority |
| `organization.change` | organization authority | | `organization.change` | organization authority |
| `instance.name` | naming authority (Webby B2, mos-1023 item 6) |
| `role.hire`, `role.fire` | hire authority | | `role.hire`, `role.fire` | hire authority |
| `review.verdict` | a chief can't pick its own reviewers, so author ≠ reviewer holds | | `review.verdict` | a chief can't pick its own reviewers, so author ≠ reviewer holds |
`roles/chief-of-staff.json` fails it (role.launch, role.end, organization.change), so the chief never hires `roles/chief-of-staff.json` fails it (role.launch, role.end, organization.change, instance.name), so the chief never hires
a chief. Reviewer and security-reviewer fail it (review.verdict). A credentialed definition is refused by a chief. Reviewer and security-reviewer fail it (review.verdict). A credentialed definition is refused by
D2.2 item 3 until row 199, and the floor list is the place a credentialed action joins if row 199 needs it. D2.2 item 3 until row 199, and the floor list is the place a credentialed action joins if row 199 needs it.
@@ -283,10 +301,10 @@ The floor is a constant in `business/src/vocabulary.mjs`, `HIRE_FLOOR`. It is ch
- At hire, in the broker, against the definition as loaded then. A role file edited after load is caught - At hire, in the broker, against the definition as loaded then. A role file edited after load is caught
here. here.
`hireable` in `roles/chief-of-staff.json` starts empty in this row (`[]`). Filling it is a reviewed role `hireable` in `roles/chief-of-staff.json` stays empty in this row (`[]`, Q4, sage-1374). Naming the first
file commit, as AGENTS.md invariant 7 requires. With an empty list the chief can hire nothing, even with role kind is a separate policy commit in `roles/`, made after Jason resolves the `prd.approve` decision, as
Jason's resolve, until a reviewed commit names a kind. I chose that over a default list because the AGENTS.md invariant 7 requires. With an empty list the chief can hire nothing, even with Jason's resolve,
choice of role kinds is a policy call, not this row's. until that commit. The row's tests use scratch role files whose `hireable` names a kind.
### D2.4 The per-agent forbid and the veto (sage-1365 item 3) ### D2.4 The per-agent forbid and the veto (sage-1365 item 3)
@@ -427,7 +445,8 @@ the other byte-identical (T11).
## D7. The Console ## D7. The Console
A new Settings section, "Agents", in its own `agents.js` file. Settings itself isn't rewritten. It lists A new Settings section, "Agents", in its own `agents.js` file. Settings itself isn't rewritten. It lists
each instance with definition, listed, kept, retired and running runs. It has these actions: each instance with definition, listed, kept, retired and running runs, and flags a run with a
`lifecycle.end.alive` event as "still running after fire" (D2.2). It has these actions:
- Create (definition select, from `definitions`), Delete (with a confirm naming what stays: the evidence - Create (definition select, from `definitions`), Delete (with a confirm naming what stays: the evidence
and the workspace under `.retired`). and the workspace under `.retired`).
@@ -468,6 +487,7 @@ Every test uses scratch roots under `TMPDIR` and the existing fixtures (`busines
| T8b | Hire floor (D2.3): one case per floor action. A fixture definition, clean when the chief's role file loads and listed in `hireable`, is rewritten to hold only that action before the hire. The hire refuses `definition-not-hireable`. chief-of-staff and reviewer refuse | same | | T8b | Hire floor (D2.3): one case per floor action. A fixture definition, clean when the chief's role file loads and listed in `hireable`, is rewritten to hold only that action before the hire. The hire refuses `definition-not-hireable`. chief-of-staff and reviewer refuse | same |
| T8c | Floor at load: a role file whose `hireable` names a definition holding each floor action refuses at `loadRole`, one case per action. A clean list loads. A missing, version 1 or duplicate name refuses | business `role.test.mjs` | | T8c | Floor at load: a role file whose `hireable` names a definition holding each floor action refuses at `loadRole`, one case per action. A clean list loads. A missing, version 1 or duplicate name refuses | business `role.test.mjs` |
| T9 | Chief fire: runs end, open requests cancelled, delisted, still in `roles`. Kept (even with a resolved decision), END_PROTECTED, `launch.by` and last-listed refuse | bus `chief-of-staff.test.mjs` | | T9 | Chief fire: runs end, open requests cancelled, delisted, still in `roles`. Kept (even with a resolved decision), END_PROTECTED, `launch.by` and last-listed refuse | bus `chief-of-staff.test.mjs` |
| T9a | Fire ends processes (F4): with a bound run and a spawned but unbound registry run of I, both fixture runners holding a stand-in model variable, fire answers both runs. After the launcher pass, neither runner pid is alive and I holds no claim. A fixture runner that ignores SIGTERM is still alive after the 10 s check and gets `lifecycle.end.alive` with its run and pid. A runner whose start time changed is not signalled | cli `launcher.test.mjs` |
| T10 | Keep: chief `role.revoke` with a reason, fire, hire of that name and an `organization.set` model write aimed at a kept instance each refuse `instance-kept`. A `role.revoke` citing a human-resolved decision ends the kept run (Q2). The human's V3 works | same | | T10 | Keep: chief `role.revoke` with a reason, fire, hire of that name and an `organization.set` model write aimed at a kept instance each refuse `instance-kept`. A `role.revoke` citing a human-resolved decision ends the kept run (Q2). The human's V3 works | same |
| T11 | Two scratch stack instances with the same business id: V2, V3 and V5 in A leave B's root and config byte-identical. A chief of business A can't hire into business B on the same bus | cli `lifecycle.test.mjs`, bus | | T11 | Two scratch stack instances with the same business id: V2, V3 and V5 in A leave B's root and config byte-identical. A chief of business A can't hire into business B on the same bus | cli `lifecycle.test.mjs`, bus |
| T12 | Agent and reader sessions refuse every human verb before any effect. The tester gets exactly the D4.1 table, including `tester-refused` for `instances.set` op `delete` (B6). The D4 hook is called with the verb | bus `instances-verb.test.mjs` | | T12 | Agent and reader sessions refuse every human verb before any effect. The tester gets exactly the D4.1 table, including `tester-refused` for `instances.set` op `delete` (B6). The D4 hook is called with the verb | bus `instances-verb.test.mjs` |
@@ -520,6 +540,11 @@ sha256 checked after each one.
| M44 | fire skips `instance-kept` when a decision is cited | T9 | | M44 | fire skips `instance-kept` when a decision is cited | T9 |
| M45 | `instance.hire` skips the `launch.by` caller check | T8 | | M45 | `instance.hire` skips the `launch.by` caller check | T8 |
| M46 | `veto.set` skips `#human` | T18 | | M46 | `veto.set` skips `#human` | T18 |
| M47 | `instance.name` removed from `HIRE_FLOOR` in the broker's hire check | T8b |
| M48 | `instance.name` removed from the load check in `role.mjs` only | T8c |
| M49 | fire revokes the claims but writes no `lifecycle.end`, so no runner is signalled | T9a |
| M50 | fire's signal skips spawned but unbound registry runs | T9a |
| M51 | the 10 s recheck writes no `lifecycle.end.alive` | T9a |
## Scope and boundaries ## Scope and boundaries
@@ -554,8 +579,11 @@ sha256 checked after each one.
- R5. `tools.mjs`, `serve.mjs`, `broker.mjs`, `vocabulary.mjs` and `role.mjs` may carry sha256 pins in - R5. `tools.mjs`, `serve.mjs`, `broker.mjs`, `vocabulary.mjs` and `role.mjs` may carry sha256 pins in
other packages' tests. The build greps the tests for every changed path plus a 64-hex value before other packages' tests. The build greps the tests for every changed path plus a 64-hex value before
in-review, without a `head`. in-review, without a `head`.
- R6. With `hireable` empty, the chief can hire nothing until a reviewed role file commit names a kind. - R6. With `hireable` empty, the chief can hire nothing until the policy commit in `roles/` that follows
Case 1's "as needed" waits on that commit and on PRD 1.7. the `prd.approve` resolve names a kind (Q4). Case 1's "as needed" waits on that commit and on PRD 1.7.
- R7. A fired agent's runner that ignores SIGTERM keeps the shared model token until the human runs
`mosaic stop` (D2.2, F4). `lifecycle.end.alive` makes that visible. It doesn't end it. A SIGKILL
escalation, or a per-agent model token (row 199 Q-M4, a later proxy row), would close it.
## Questions and rulings ## Questions and rulings
@@ -565,8 +593,9 @@ sha256 checked after each one.
branch. D2.4. branch. D2.4.
- Q3, ruled by Sage (sage-1355): launch and terminate are tester verbs scoped to the tester's own - Q3, ruled by Sage (sage-1355): launch and terminate are tester verbs scoped to the tester's own
business, behind `bus.tester`. D4.1. business, behind `bus.tester`. D4.1.
- Q4, for Sage: should `hireable` start empty (my choice, D2.3), or should this row name a first kind for - Q4, ruled by Sage (sage-1374): `hireable` stays empty in this row. Naming the first role kind is a
review? separate policy commit in `roles/` after Jason resolves the `prd.approve` decision. Tests use scratch role
files that name a kind. D2.3.
## r2 changes against webby-0003 ## r2 changes against webby-0003
@@ -589,3 +618,12 @@ sha256 checked after each one.
| N6 | V3 and V4: no SIGKILL, `regenerate-timeout` | | N6 | V3 and V4: no SIGKILL, `regenerate-timeout` |
| N7 | D2.4: intended, Q169 | | N7 | D2.4: intended, Q169 |
| N8 | V2 item 2 and T2 | | N8 | V2 item 2 and T2 |
## r3 changes against sage-1374, sage-1377 and Megavolt's F4
| Item | Where |
|---|---|
| Q4 ruled: `hireable` stays empty, the first kind is a later policy commit in `roles/` | D2.3, R6, Questions |
| `instance.name` joins the floor (Webby B2, mos-1023 item 6) | D2.3 table, T8b, T8c, M47, M48 |
| F4: fire ends the fired agent's processes, since `ENV_ALLOW` gives every run the shared model token | D2.2, D7, T9a, M49 to M51, R7 |
| `git.push.remote` routed to coder only (mos-1028 item 3, row 199 r3) | `PRD-1.7-DRAFT.md`, REQ-LAND-2 and the REQ-WAIT-3 append |
+42 -13
View File
@@ -1,4 +1,4 @@
# PRD 1.7 draft: the chief of staff's hire and fire authority # PRD 1.7 draft: the chief of staff's hire and fire authority, and the coder's push to its fork
Draft by Honker for row 194 (#1692), under Mos's ruling mos-1017 (through Sage, sage-1365). Not approved. Draft by Honker for row 194 (#1692), under Mos's ruling mos-1017 (through Sage, sage-1365). Not approved.
It changes nothing until Jason approves it as a `prd.approve` decision in the Console. Sage opens that It changes nothing until Jason approves it as a `prd.approve` decision in the Console. Sage opens that
@@ -6,27 +6,31 @@ decision and sends Mos its id. Base: `docs/prd/mosaic-stack.md` version 1.6 at 5
## What it changes ## What it changes
Row 194 adds `role.hire` and `role.fire` to the vocabulary. Under REQ-WAIT-3 they are hard waits, so today Row 194 adds `role.hire` and `role.fire` to the vocabulary, and row 199 adds `git.push.remote`. Under
Jason resolves every hire and every fire. This version routes them for the chief of staff within fixed REQ-WAIT-3 all three are hard waits, so today Jason resolves every hire, every fire and every push to a
bounds, so the chief hires and fires as needed and the user's per-agent forbid still holds. remote. This version routes hire and fire for the chief of staff within fixed bounds, so the chief hires
and fires as needed and the user's per-agent forbid still holds. It routes `git.push.remote` for the coder
only (Mos, mos-1028 item 3). One `prd.approve` decision covers all three (Vikunja #420).
When this version lands, a reviewed commit: When this version lands, a reviewed commit:
1. adds `role.hire` and `role.fire` to `ROUTED` in `packages/business/src/vocabulary.mjs` and in 1. adds `role.hire`, `role.fire` and `git.push.remote` to `ROUTED` in
`packages/bus/src/broker.mjs`. `packages/business/src/vocabulary.mjs` and in `packages/bus/src/broker.mjs`.
2. adds both to `roles/chief-of-staff.json` withinRole. 2. adds `role.hire` and `role.fire` to `roles/chief-of-staff.json` withinRole, and `git.push.remote` to
`roles/coder.json` withinRole. No other role file gets `git.push.remote`.
3. switches the broker's check on `instance.hire` and `instance.fire` from a cited decision to the 3. switches the broker's check on `instance.hire` and `instance.fire` from a cited decision to the
`reason` form, as `role.end` works for REQ-ROLE-2. `reason` form, as `role.end` works for REQ-ROLE-2, and makes `publish`'s `decision` optional for a
within-role class (row 199 D9 step 1).
Every other check stays as row 194 builds it. Filling `hireable` stays a separate reviewed role file Every other check stays as rows 194 and 199 build it. Filling `hireable` stays a separate reviewed role
commit. file commit in `roles/`.
## Text for the PRD ## Text for the PRD
Status line, appended to the current paragraph: Status line, appended to the current paragraph:
> Version 1.7 routes the chief of staff's hire and fire actions, approved by Jason in decision > Version 1.7 routes the chief of staff's hire and fire actions and the coder's push to its own fork,
> <prd.approve decision id>. > approved by Jason in decision <prd.approve decision id>.
Source, a new bullet above "Version 1.6 source": Source, a new bullet above "Version 1.6 source":
@@ -46,7 +50,7 @@ Under "Added in version 1.2", after REQ-COS-2:
> - The chief hires only role definitions that need no credentials, until a later version adds > - The chief hires only role definitions that need no credentials, until a later version adds
> credentialed roles. > credentialed roles.
> - The chief hires only the role kinds its role file lists. Changing that list is a reviewed commit. > - The chief hires only the role kinds its role file lists. Changing that list is a reviewed commit.
> - The chief never hires a role that holds launch, end, organization or hire authority, or the > - The chief never hires a role that holds launch, end, organization, naming or hire authority, or the
> review verdict. So the chief never hires a chief and never picks its own reviewers, and the > review verdict. So the chief never hires a chief and never picks its own reviewers, and the
> author is never the reviewer. This holds whatever the list says. > author is never the reviewer. This holds whatever the list says.
> - The user can forbid per agent. The user marks an instance so the chief can't fire, replace or end > - The user can forbid per agent. The user marks an instance so the chief can't fire, replace or end
@@ -55,9 +59,28 @@ Under "Added in version 1.2", after REQ-COS-2:
> - The kill switch stops every hire and fire while it is on. > - The kill switch stops every hire and fire while it is on.
> - Every hire and fire is logged with the chief's instance, its run and the reason. > - Every hire and fire is logged with the chief's instance, its run and the reason.
Under "Landing", after REQ-LAND-1:
> Added in version 1.7:
>
> - **REQ-LAND-2.** A coder pushes its own work to its own fork. The action is `git.push.remote`.
> - What it pushes: the latest work commit the bus recorded for the coder's task, to a remote its
> business grants it.
> - Where it lands: only the branch `refs/heads/<its Gitea user>/<branch>`. The host builds that
> name, the agent never does. A push never forces.
> - Which remote: a fork that the agent's own Gitea user owns. Gitea itself then refuses a push to
> upstream or to another agent's fork.
> - Who may: the action routes under REQ-DEC-2 and is granted within-role to the coder role only. No
> other role file grants it.
> - Before version 1.7 the action was a hard wait under REQ-WAIT-3, so each push needed a decision
> the human resolved.
>
> Every push is logged with the remote, the ref and the commit.
REQ-WAIT-3, appended: REQ-WAIT-3, appended:
> `role.hire` and `role.fire` were added as hard waits and route under REQ-COS-3 since version 1.7. > `role.hire` and `role.fire` were added as hard waits and route under REQ-COS-3 since version 1.7.
> `git.push.remote` was added as a hard wait and routes under REQ-LAND-2 since version 1.7.
## Notes for the reviewer and for Mos ## Notes for the reviewer and for Mos
@@ -67,6 +90,12 @@ REQ-WAIT-3, appended:
user-resolved decision still ends a kept run (row 194 D2.4). user-resolved decision still ends a kept run (row 194 D2.4).
- The kill switch is row 191's. If row 191 hasn't landed when this version is approved, the bound still - The kill switch is row 191's. If row 191 hasn't landed when this version is approved, the bound still
holds through row 194's chain on its candidate. holds through row 194's chain on its candidate.
- REQ-LAND-2 follows row 199's design r3 (c2c729ddf, D9 to D11, Q-M1 and Q-M2) and Megavolt's
wording in megavolt-0005, through Sage (sage-1377). Megavolt checks the text against that design.
- REQ-LAND-2 and REQ-COS-3 meet at the coder. Coder declares Gitea credentials, so REQ-COS-3's
credentials bound keeps the chief from hiring one until a later version lifts it. Granting
`git.push.remote` to coder doesn't put it on the hire floor, because a push reaches only the agent's
own fork.
- The credentials bound names no row number, so the text doesn't go stale when row 199 lands. A later - The credentials bound names no row number, so the text doesn't go stale when row 199 lands. A later
version lifts it. version lifts it.
- This draft follows the PRD's own style: plain words, no semicolons in new sentences, and the status - This draft follows the PRD's own style: plain words, no semicolons in new sentences, and the status