honker: row 194 design r4 (webby-0010/0011: HIRE_ALLOWED allowlist, decision field compare, fire order store-first; sage-1380: fire SIGKILL via row 191 stop; megavolt-0008: REQ-LAND-2 oid binding, row 199 r4)

This commit is contained in:
jason.woltje committed 2026-10-20 14:35:12 -05:00
1 parent d0afda0243
commit 7d07882362
2 files changed
+154 -62

No files matched your search

+122 -44
View File
@@ -18,8 +18,9 @@ Related designs read: row 190 `agents/darkwing/work/queue-190/DESIGN.md` (r2, af
- The chief hires (adds and lists an instance) and fires (ends its runs and delists it). It never deletes. - The chief hires (adds and lists an instance) and fires (ends its runs and delists it). It never deletes.
`role.hire` and `role.fire` are new hard waits, so Jason resolves each one (Mos, mos-1017). The routed `role.hire` and `role.fire` are new hard waits, so Jason resolves each one (Mos, mos-1017). The routed
form waits on a PRD 1.7 draft in this packet. form waits on a PRD 1.7 draft in this packet.
- A hire floor in code: the chief can never hire a definition that holds launch, end, organization or hire - A hire allowlist in code: the chief can hire only a definition whose every action is on `HIRE_ALLOWED`
authority, or `review.verdict`. (message, record, review request, assigned-task updates, and two actions that matter after row 199).
Launch, end, organization, naming, hire, verdict, merge and every later action stay out.
- The per-agent forbid is two lists in the business file that only the human writes: `launch.keep` - The per-agent forbid is two lists in the business file that only the human writes: `launch.keep`
(instances the chief can't fire, replace or end) and `launch.noHire` (definitions the chief can't hire). (instances the chief can't fire, replace or end) and `launch.noHire` (definitions the chief can't hire).
- The broker reloads a business from its file after each committed write. Create, delete, hire, fire and - The broker reloads a business from its file after each committed write. Create, delete, hire, fire and
@@ -205,8 +206,11 @@ The flow in this row:
2. Jason resolves it in the Console or the CLI. 2. Jason resolves it in the Console or the CLI.
3. The chief calls `instance.hire` or `instance.fire` citing the decision. The broker checks it with 3. The chief calls `instance.hire` or `instance.fire` citing the decision. The broker checks it with
`#consumeAuthority(s, 'role.hire', { decision, target })`, as the `role.revoke` decision branch does, `#consumeAuthority(s, 'role.hire', { decision, target })`, as the `role.revoke` decision branch does,
and the arguments must equal the decision body. Every check below runs again at this call, so a veto and the arguments must equal the decision body field by field (N1, webby-0010). For hire that is
set after the resolve still refuses. `name`, `definition` and `vars` (vars compared with `canonicalJson`, absent equal to absent only). For
fire it is `name` and `reason`. A key in the call that the body lacks, or a body key the call lacks,
refuses `decision-mismatch`. Every check below runs again at this call, so a veto set after the resolve
still refuses.
The routed form, where the chief hires and fires without a decision each time, needs a PRD version that The routed form, where the chief hires and fires without a decision each time, needs a PRD version that
moves both actions to `ROUTED`. My draft is `agents/honker/work/queue-194/PRD-1.7-DRAFT.md`. It quotes moves both actions to `ROUTED`. My draft is `agents/honker/work/queue-194/PRD-1.7-DRAFT.md`. It quotes
@@ -214,7 +218,7 @@ TARGET.md line 66 and carries Mos's bounds. Jason approves it as a `prd.approve`
and Sage opens that decision. When it lands, a later commit adds the two actions to both `ROUTED` lists and Sage opens that decision. When it lands, a later commit adds the two actions to both `ROUTED` lists
(`vocabulary.mjs`, `broker.mjs:43-69`) and to `roles/chief-of-staff.json` withinRole. The broker then (`vocabulary.mjs`, `broker.mjs:43-69`) and to `roles/chief-of-staff.json` withinRole. The broker then
takes the `reason` form instead of a decision. The verbs and every check below stay the same. This row takes the `reason` form instead of a decision. The verbs and every check below stay the same. This row
builds and tests the hard-wait form only. It writes the `hireable` key and its floor now (D2.3), because builds and tests the hard-wait form only. It writes the `hireable` key and its allowlist now (D2.3), because
both forms use them. both forms use them.
### D2.2 What the chief may do ### D2.2 What the chief may do
@@ -236,7 +240,8 @@ with actor the chief's instance and run, and the decision id.
Hire refuses, in order: Hire refuses, in order:
1. `definition-not-hireable` when the definition fails the floor (D2.3), whatever any list says. 1. `definition-not-hireable` when the definition holds an action outside `HIRE_ALLOWED` (D2.3), whatever
any list says.
2. `definition-not-hireable` when the definition isn't in the chief role file's `hireable` list, or is in 2. `definition-not-hireable` when the definition isn't in the chief role file's `hireable` list, or is in
the business's `launch.noHire`. the business's `launch.noHire`.
3. `organization-definition-refused` for END_PROTECTED, `organization-credentials-out-of-scope` for a 3. `organization-definition-refused` for END_PROTECTED, `organization-credentials-out-of-scope` for a
@@ -259,43 +264,91 @@ name.
Fire ends the fired instance's processes, not only its claims (F4, Megavolt through sage-1374). Every run 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 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 `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 can't take that token back, so a fired agent whose process lives on still holds it.
steps 1 to 3 in the same transaction as the delist:
The delist is a business file commit through the organization writer, and the revoke and events are store
rows. No transaction covers both (webby-0011 B2), so fire runs in a fixed order, store side first:
A. One store transaction: steps 1 to 3 below, plus `fire.stopped` with the decision id. The decision isn't
consumed yet.
B. The delist through the organization writer (`launch.instances` loses I, logged in `changes.jsonl`).
C. One store write consuming the decision and recording `fire.done`.
If B or C fails, fire answers the step's code and the decision stays unconsumed. A second fire citing the
same decision runs again. A finds nothing to revoke, cancel or stop, and that isn't a refusal for fire
(unlike V3's `not-held`). B finds I already delisted and counts as done. C consumes. So a fire is safe to
repeat until it's consumed. The other order would leave a delisted agent with a live process and its model
token when the store step fails, which is the case F4 exists to stop. A halt between A and B doesn't stop
B or C, because neither starts anything. A second fire while halted refuses (D6), and the halt's own stop
covers the runs.
Steps 1 to 3 are V3's, with one change:
1. It revokes every claim the instance holds. 1. It revokes every claim the instance holds.
2. It writes `lifecycle.cancelled` for each of the instance's open requests. 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 3. It writes `lifecycle.end` with the runs and `kill: true`. The launcher pass stops each runner, spawned
start-time check (`launcher.mjs:526`), spawned but unbound registry runs included. but unbound registry runs included, with escalation (sage-1380 item 1):
- SIGTERM after the identity check.
- After the grace, SIGKILL to any runner still alive, after the same identity check again, and
`lifecycle.end.killed` with the run and pid.
- A runner still alive after SIGKILL and the observe wait gets `lifecycle.end.alive`. The Console's
Agents section shows it to the human.
A runner whose identity no longer matches is never signalled. Every check, the recheck after the
grace included, uses the identity (start ticks plus boot id), never a bare pid, so a reused pid never
counts as the fired run (webby-0011 N2).
Fire's answer lists the runs it signalled. The delist stops any new chief launch (`launch` takes listed Fire's answer lists the runs it is stopping. 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 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 fire answer names it, and the human's terminate ends its run.
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) The stop is row 191's per-run stop, not a second one, so the two can't fight (sage-1380 item 1). Row 191's
design r2 (`agents/rocko/work/queue-191/DESIGN-r2.md`, "Stop policy and completion") sends SIGTERM, waits
5 s, sends SIGKILL to verified groups and namespaces, and observes for 2 s. Its identity check is start
ticks plus boot id, which replaces the start-time check at `launcher.mjs:526`. Fire calls that routine for
one instance's runs and uses its constants, so the grace is row 191's 5 s, not the 10 s in r3. The order:
A definition the chief can never hire, whatever `hireable` says. It fails the floor when its withinRole or - Fire is refused while halted (D6), so a halt sweep never meets a fire that started after it.
crossRole holds any of: - A halt that comes during a fire's stop finds those runs already stopping. Each run has one stop receipt
(row 191's), and the first stop owns it. The halt sweep joins it instead of sending a second SIGTERM or
restarting the deadline, and counts the run in its remaining or quiescent totals.
- The record says which stop owned it: reason `fire` with the decision, or row 191's `kill-switch` with
its generation.
| Floor action | Why | V3 terminate is unchanged. It still sends SIGTERM only (N6). Only fire escalates, because only a fired
agent must lose the token at once. If row 191 hasn't landed when this row builds, the build chains on its
candidate (Q177), as D6 already says.
### D2.3 The hire allowlist (B2, sage-1365 item 4, webby-0010 B1)
r2 and r3 had a deny list, `HIRE_FLOOR`. Webby's B1 showed it missed routed actions (`git.merge.protected`,
which `roles/merge-gate.json` holds with no credentials, `decision.resolve.technical`, `policy.change`,
`prd.approve`), and that any action added later would be hireable by default. That isn't fail-closed. r4
replaces it with an allowlist.
A definition is hireable only if every action in its withinRole and crossRole is on `HIRE_ALLOWED`:
| Allowed action | Held today by |
|---|---| |---|---|
| `role.launch` | launch authority | | `message.send`, `record.write` | every v2 role file |
| `role.end`, `role.revoke` | end authority | | `review.request`, `task.update.assigned` | coder, cto, pm, reviewer |
| `organization.change` | organization authority | | `task.scope.change`, `git.push.working` | coder, cto, pm (credentialed, so they matter only after row 199) |
| `instance.name` | naming authority (Webby B2, mos-1023 item 6) |
| `role.hire`, `role.fire` | hire authority |
| `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, instance.name), so the chief never hires Everything else in `ACTIONS` fails, including the actions of the old floor (`role.launch`, `role.end`,
a chief. Reviewer and security-reviewer fail it (review.verdict). A credentialed definition is refused by `role.revoke`, `organization.change`, `instance.name`, `review.verdict`, `role.hire`, `role.fire`) and those
D2.2 item 3 until row 199, and the floor list is the place a credentialed action joins if row 199 needs it. B1 named. An action added to `ACTIONS` later fails until a reviewed commit adds it here.
`git.push.remote` (row 199, routed to coder by PRD 1.7) isn't on the list. A coder holding it stays
unhireable until a reviewed commit adds it, after the credentials bound is lifted.
The floor is a constant in `business/src/vocabulary.mjs`, `HIRE_FLOOR`. It is checked twice: Against today's role files: chief-of-staff, cto, merge-gate, pm, reviewer and security-reviewer fail.
Assistant, researcher, coder and the five C-suite files pass the allowlist. Assistant is END_PROTECTED and
coder is credentialed, so D2.2 item 3 refuses them. The C-suite files hold only `message.send` and
`record.write`, so hiring one gives no authority beyond its name. `hireable` still has to list it.
The list is a constant in `business/src/vocabulary.mjs`, `HIRE_ALLOWED`. It is checked twice:
- At load. `role.mjs` validates the chief role file's `hireable` list: distinct definition names, each - At load. `role.mjs` validates the chief role file's `hireable` list: distinct definition names, each
present in `rolesDir`, version 2, and not failing the floor. A list naming one refuses the role file present in `rolesDir`, version 2, and every action on `HIRE_ALLOWED`. A list naming one that isn't refuses
(`hireable names <def>, which holds <action>`). The check reads only the named file's authority lists, the role file (`hireable names <def>, which holds <action>`). The check reads only the named file's authority lists,
never its `hireable`, so a list can't recurse. `hireable` is an optional key on any role file. This is a never its `hireable`, so a list can't recurse. `hireable` is an optional key on any role file. This is a
role schema change, named for review. role schema change, named for review.
- 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
@@ -484,10 +537,12 @@ Every test uses scratch roots under `TMPDIR` and the existing fixtures (`busines
| T7 | V6: each op, the no-op refusal, an unknown `noHire` name accepted, and a `changes.jsonl` entry with actor `human` | business | | T7 | V6: each op, the no-op refusal, an unknown `noHire` name accepted, and a `changes.jsonl` entry with actor `human` | business |
| T8 | Chief hire (hard-wait form): no decision refuses. A decision raised by the chief goes to the human only, no arbiter. After the resolve, the hire adds and lists the instance, logs actor and decision, and the same chief session launches it (D3 item 5). Arguments that differ from the decision body refuse. A non-chief caller with a resolved decision refuses `role-not-chief`. Outside `hireable`, in `noHire`, credentialed, END_PROTECTED, retired, and extra vars each refuse | bus `chief-of-staff.test.mjs` | | T8 | Chief hire (hard-wait form): no decision refuses. A decision raised by the chief goes to the human only, no arbiter. After the resolve, the hire adds and lists the instance, logs actor and decision, and the same chief session launches it (D3 item 5). Arguments that differ from the decision body refuse. A non-chief caller with a resolved decision refuses `role-not-chief`. Outside `hireable`, in `noHire`, credentialed, END_PROTECTED, retired, and extra vars each refuse | bus `chief-of-staff.test.mjs` |
| T8a | Re-hire (B3): a fired name with the same definition and no vars is listed again. Another definition or vars refuses `organization-instance-exists` | same | | T8a | Re-hire (B3): a fired name with the same definition and no vars is listed again. Another definition or vars refuses `organization-instance-exists` | 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 | | T8b | Hire allowlist (D2.3): one case per action in `ACTIONS` that is neither on `HIRE_ALLOWED` nor a hard wait (`role.mjs` already refuses those), looped from the lists, not typed by hand. A fixture definition, clean when the chief's role file loads and listed in `hireable`, is rewritten to hold that action before the hire. The hire refuses `definition-not-hireable`. A definition holding one allowed and one refused action refuses. One holding only allowed actions hires. A pin test fixes `HIRE_ALLOWED` to the six actions | 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 | Allowlist at load: a role file whose `hireable` names a definition holding each refused action refuses at `loadRole`, one case per action, looped the same way. The mixed case refuses. 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` | | T9a | Fire ends processes (F4, sage-1380 item 1): 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 gets SIGKILL after the grace, is gone, and has `lifecycle.end.killed` with its run and pid. A stand-in whose kill is stubbed to fail gets `lifecycle.end.alive`. A runner whose identity changed before the SIGTERM, or between SIGTERM and SIGKILL, gets no signal. V3 terminate of a SIGTERM-ignoring runner sends no SIGKILL | cli `launcher.test.mjs` |
| T9b | Fire and row 191's halt: a halt during a fire's stop sends no second SIGTERM, keeps the fire's deadline, writes one stop receipt with reason `fire` and the decision, and counts the run in its totals. A fire while halted refuses (D6) | cli `launcher.test.mjs`, on row 191's fixture |
| T9c | Fire order (webby-0011 B2): the organization writer stubbed to fail after A commits. Fire answers the writer's code, I's claims are revoked and its runs stopped, I is still listed, and the decision is unconsumed. A second fire citing the same decision delists I and consumes it. A third fire refuses `decision-consumed`. With C stubbed to fail, the second fire finds I delisted and only consumes | bus `chief-of-staff.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` |
@@ -535,16 +590,23 @@ sha256 checked after each one.
| M27 | the `keep` check is dropped from `organization.set`'s model write | T10 | | M27 | the `keep` check is dropped from `organization.set`'s model write | T10 |
| M28 | the `keep` check is added to the `role.revoke` decision branch | T10 | | M28 | the `keep` check is added to the `role.revoke` decision branch | T10 |
| M29 | a re-hire accepts a different definition | T8a | | M29 | a re-hire accepts a different definition | T8a |
| M30 to M36 | each floor action removed from `HIRE_FLOOR` in the broker's hire check, one mutant per action: `role.launch`, `role.end`, `role.revoke`, `organization.change`, `role.hire`, `role.fire`, `review.verdict` | T8b | | M30 to M36 | the broker's hire check accepts one action outside `HIRE_ALLOWED`, one mutant per action: `role.launch`, `role.end`, `organization.change`, `instance.name`, `review.verdict`, `git.merge.protected`, `decision.resolve.technical` | T8b |
| M37 to M43 | the same seven, removed from the load check in `role.mjs` only | T8c | | M37 to M43 | the same seven, accepted by the load check in `role.mjs` only | T8c |
| 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 | | M47 | the broker's hire check needs some action on `HIRE_ALLOWED` instead of every action | T8b |
| M48 | `instance.name` removed from the load check in `role.mjs` only | T8c | | M48 | the same in `role.mjs` only | T8c |
| M49 | fire revokes the claims but writes no `lifecycle.end`, so no runner is signalled | T9a | | 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 | | M50 | fire's signal skips spawned but unbound registry runs | T9a |
| M51 | the 10 s recheck writes no `lifecycle.end.alive` | T9a | | M51 | fire's stop drops the SIGKILL escalation | T9a |
| M52 | SIGKILL is sent without the second identity check | T9a |
| M53 | a killed runner gets no `lifecycle.end.killed` | T9a |
| M54 | V3 terminate escalates to SIGKILL as fire does | T9a |
| M55 | the halt sweep sends its own SIGTERM and restarts the deadline on a run fire is stopping | T9b |
| M56 | fire delists before the store step | T9c |
| M57 | fire consumes the decision in step A | T9c |
| M58 | a second fire with nothing to revoke refuses `not-held` | T9c |
## Scope and boundaries ## Scope and boundaries
@@ -565,8 +627,8 @@ sha256 checked after each one.
## Risks ## Risks
- R1. Case 1's working roles (coder, reviewer, pm, cto) all declare credentials (E11), and reviewer is - R1. Case 1's working roles (coder, reviewer, pm, cto) all declare credentials (E11), and pm, cto and
below the hire floor anyway. Neither the human verb nor the chief can create or delete credentialed reviewer fail the hire allowlist anyway. Neither the human verb nor the chief can create or delete credentialed
roles until the credentials row (row 199). That is the largest gap against Case 1, and it isn't this roles until the credentials row (row 199). That is the largest gap against Case 1, and it isn't this
row's to close. row's to close.
- R2. The human proof is path-bound and same-uid (P19 333). Any same-uid process that runs the tree's - R2. The human proof is path-bound and same-uid (P19 333). Any same-uid process that runs the tree's
@@ -581,9 +643,10 @@ sha256 checked after each one.
in-review, without a `head`. in-review, without a `head`.
- R6. With `hireable` empty, the chief can hire nothing until the policy commit in `roles/` that follows - R6. With `hireable` empty, the chief can hire nothing until the policy commit in `roles/` that follows
the `prd.approve` resolve names a kind (Q4). 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 - R7. Fire's SIGKILL closes the SIGTERM-ignoring case (sage-1380). A runner still alive after SIGKILL
`mosaic stop` (D2.2, F4). `lifecycle.end.alive` makes that visible. It doesn't end it. A SIGKILL (an unkillable process, or a stop whose identity check fails) keeps the shared model token until the
escalation, or a per-agent model token (row 199 Q-M4, a later proxy row), would close it. human acts on `lifecycle.end.alive`. Only a per-agent model token (row 199 Q-M4, a later proxy row)
would let a fire revoke the token itself.
## Questions and rulings ## Questions and rulings
@@ -602,7 +665,7 @@ sha256 checked after each one.
| Finding | Where | | Finding | Where |
|---|---| |---|---|
| B1 | D2.1: hard waits under REQ-WAIT-3, no role grant. The routed form is PRD 1.7 (packet draft) | | B1 | D2.1: hard waits under REQ-WAIT-3, no role grant. The routed form is PRD 1.7 (packet draft) |
| B2 | D2.3: the floor from sage-1365 item 4, checked at load and at hire, T8b, T8c, M30 to M43 | | B2 | D2.3: the floor from sage-1365 item 4, checked at load and at hire, T8b, T8c, M30 to M43 (an allowlist since r4) |
| B3 | D2.2 hire item 5: a re-hire keeps the definition, T8a, M29 | | B3 | D2.2 hire item 5: a re-hire keeps the definition, T8a, M29 |
| B4 | D1.1: one `lifecycleTake` transaction re-checks and writes `lifecycle.taken`, T2a, M4, M22 | | B4 | D1.1: one `lifecycleTake` transaction re-checks and writes `lifecycle.taken`, T2a, M4, M22 |
| B5 | V3 cancels open requests and signals unbound runs. V5 refuses `instance-starting`, runs under `serialize`, commits the file before the rename, and states the recovery. T4, T6, M23 to M25 | | B5 | V3 cancels open requests and signals unbound runs. V5 refuses `instance-starting`, runs under `serialize`, commits the file before the rename, and states the recovery. T4, T6, M23 to M25 |
@@ -627,3 +690,18 @@ sha256 checked after each one.
| `instance.name` joins the floor (Webby B2, mos-1023 item 6) | D2.3 table, T8b, T8c, M47, M48 | | `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 | | 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 | | `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 |
## r4 changes against webby-0010, sage-1380 and megavolt-0008
| Item | Where |
|---|---|
| webby-0010 B1: the deny-list floor becomes the allowlist `HIRE_ALLOWED`, so merge-gate, `decision.resolve.technical`, `policy.change`, `prd.approve` and any later action are refused | D2.3, Summary, R1, T8b, T8c, M30 to M43, M47, M48, PRD REQ-COS-3 bound |
| webby-0010 N1: field-by-field compare of the call against the decision body, extra or missing keys refuse | D2.1 item 3 |
| webby-0010 N2: the model write is in REQ-COS-3's text | `PRD-1.7-DRAFT.md` |
| sage-1380 item 1: fire escalates to SIGKILL through row 191's per-run stop, and the order against a halt is fixed | D2.2, T9a, T9b, M51 to M55, R7 |
| sage-1380 item 2 and megavolt-0008: REQ-LAND-2 binds one commit oid, and Megavolt's three text changes against row 199 r4 (e1aa6216c) | `PRD-1.7-DRAFT.md` |
| webby-0011 B1: same as webby-0010 B1, M47 and M48 now allowlist mutants | D2.3 |
| webby-0011 B2: fire runs store side first, then the delist, then the consume, safe to repeat | D2.2 order A to C, T9c, M56 to M58 |
| webby-0011 N1: the remote-owner host check at each push | sent to Megavolt for row 199 |
| webby-0011 N2: identity, not bare pid, at every check | D2.2 step 3 |
| Q4: Webby's view matches Sage's ruling (empty) | no change |
+32 -18
View File
@@ -50,11 +50,16 @@ 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, naming or hire authority, or the > - The chief hires only a role whose every action is on a fixed list of plain work actions: sending
> review verdict. So the chief never hires a chief and never picks its own reviewers, and the > messages, writing records, asking for review, updating its assigned tasks, and the scope and
> author is never the reviewer. This holds whatever the list says. > working-push actions of an assigned task. A role holding any other action, such as launch, end,
> - The user can forbid per agent. The user marks an instance so the chief can't fire, replace or end > organization, naming, hire, review verdict or merge authority, or any action added later, is never
> it, and marks role kinds the chief can't hire. Only the user changes these marks, through the > hired. So the chief never hires a chief, never picks its own reviewers or merge gate, and the author
> is never the reviewer. This holds whatever the role file's list says. Changing the fixed list is a
> reviewed commit.
> - The chief may set a hired instance's model and narrow its limits, and nothing else about it.
> - The user can forbid per agent. The user marks an instance so the chief can't fire it, end its runs
> or change its model, and marks role kinds the chief can't hire. Only the user changes these marks, through the
> WebUI or the CLI. > WebUI or the CLI.
> - 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.
@@ -64,18 +69,20 @@ Under "Landing", after REQ-LAND-1:
> Added in version 1.7: > Added in version 1.7:
> >
> - **REQ-LAND-2.** A coder pushes its own work to its own fork. The action is `git.push.remote`. > - **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 > - What it pushes: one commit, by its oid, never a ref name. That commit is the latest work commit the
> business grants it. > bus recorded for the coder's task, and the coder made that push. A decision that authorizes a push
> names that commit, and it doesn't authorize any other.
> - Where it lands: only the branch `refs/heads/<its Gitea user>/<branch>`. The host builds that > - 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. > 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 > - Which remote: the one the business file grants that agent for that project, over https, on the
> upstream or to another agent's fork. > granted host and port. It is a fork the agent's own Gitea user owns. The agent's Gitea user has no
> write access upstream, so Gitea refuses a push there as well.
> - Who may: the action routes under REQ-DEC-2 and is granted within-role to the coder role only. No > - 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. > 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 > - Before version 1.7 the action was a hard wait under REQ-WAIT-3, so each push needed a decision
> the human resolved. > the human resolved, and that decision named the commit.
> >
> Every push is logged with the remote, the ref and the commit. > Every push is logged with the remote, the ref, the commit and, when one was given, the decision.
REQ-WAIT-3, appended: REQ-WAIT-3, appended:
@@ -86,16 +93,23 @@ REQ-WAIT-3, appended:
- REQ-COS-1 (1.2) already says the chief "hires and terminates role instances". REQ-COS-3 gives that - REQ-COS-1 (1.2) already says the chief "hires and terminates role instances". REQ-COS-3 gives that
sentence its actions and bounds. It doesn't widen REQ-COS-1. sentence its actions and bounds. It doesn't widen REQ-COS-1.
- "Replace" in the forbid covers ending a kept instance's run with a reason and changing its model. A - The forbid now names what r3 called "replace": ending a kept instance's run with a reason and changing
user-resolved decision still ends a kept run (row 194 D2.4). its model (webby-0010 N2). A user-resolved decision still ends a kept run (row 194 D2.4).
- The fixed list is `HIRE_ALLOWED` in row 194 D2.3, an allowlist since r4 (webby-0010 B1). r3's text
named the refused authorities instead, which let merge-gate and any later action through.
- 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 - REQ-LAND-2 follows row 199's design r4 (e1aa6216c, D9 to D11, Q-M1 and Q-M2, and the oid binding from
wording in megavolt-0005, through Sage (sage-1377). Megavolt checks the text against that design. mos-1034 item 5) and Megavolt's wording in megavolt-0005 and megavolt-0008, through Sage (sage-1377,
sage-1380). Commit-email and shared-history checks are host checks with tests in row 199, not bounds,
so they stay out of the text (megavolt-0008).
- Once the routing lands and the decision is optional for the within-role class, the commit is the task's
latest recorded push at check time. It is still checked against the work ref tip and pushed by oid, so
the binding holds without a decision (megavolt-0008).
- REQ-LAND-2 and REQ-COS-3 meet at the coder. Coder declares Gitea credentials, so REQ-COS-3's - 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 credentials bound keeps the chief from hiring one until a later version lifts it. `git.push.remote` isn't
`git.push.remote` to coder doesn't put it on the hire floor, because a push reaches only the agent's on REQ-COS-3's fixed list either, so a coder holding it stays unhireable until a reviewed commit adds it
own fork. (row 194 D2.3).
- 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