honker: row 194 design, agent lifecycle and per-agent veto (#1692)
This commit is contained in:
1 parent
53bb594621
commit
8f80c278bf
1 file changed
+363
@@ -0,0 +1,363 @@
|
||||
# Row 194 design: agent lifecycle from the Console and the CLI, with a per-agent veto
|
||||
|
||||
Brief: `docs/plans/2026-10-20_agent-lifecycle.md` (blob a907109409ac). Gitea #1692. MVP T1 item 5 (mos-0974,
|
||||
mos-0977, DECISIONS Q176). Owner Honker, reviewer Webby, gate owner Sage. Base: refactor at 53bb59462.
|
||||
Branch `honker/row194`.
|
||||
|
||||
Related designs read: row 190 `agents/darkwing/work/queue-190/DESIGN.md` (r2, after mos-0986), row 191
|
||||
`agents/rocko/work/queue-191/DESIGN-r2.md`. Row 193 is still in design. All line numbers below are at
|
||||
53bb59462.
|
||||
|
||||
## Summary
|
||||
|
||||
- One verb family on the CLI, `mosaic business instance <verb> BUSINESS INSTANCE`, and one Console section.
|
||||
Create and delete change the business file. Launch, terminate and regenerate act on runs.
|
||||
- Every human verb goes through the bus with the human proof. Launch and regenerate go through the
|
||||
launcher's one `start()`, so `--no-launch`, the limits, `launch.revoke` and row 191's switch all apply.
|
||||
- The chief hires (adds and lists an instance) and fires (ends its runs and delists it). It never deletes.
|
||||
The human's veto is two lists in the business file: `launch.keep` (instances the chief can't touch)
|
||||
and `launch.noHire` (definitions the chief can't hire). Only a human verb writes them.
|
||||
- The broker reloads a business from its file after each committed organization write. Create, delete,
|
||||
hire, fire and veto changes then count without a bus restart. Hand edits still need one.
|
||||
- No bus schema change and no migration. One business-file schema change (D2.3) and one role file change
|
||||
(D2.1, Q1) are called out for review.
|
||||
|
||||
## What exists today (checked at 53bb59462)
|
||||
|
||||
| # | Fact | Where |
|
||||
|---|---|---|
|
||||
| E1 | Instances are keys of the business file's `roles`. `launch.instances` lists those the chief may launch. The list must be non-empty | `business.mjs:121-132` |
|
||||
| E2 | The human adds an instance and changes its definition, vars and listing. No delete. Credentialed definitions are refused | `instance-settings.mjs:1-5, 22-31, 75-156` |
|
||||
| E3 | The broker copies the businesses once at start and refuses a role it doesn't know | `broker.mjs:208, 446` |
|
||||
| E4 | The chief launches over `launch.sock`. `fromSession` checks identity, then `check()` (which reloads the file), then `authorizeLaunch`, then `start()` | `launcher.mjs:232-327, 624-668` |
|
||||
| E5 | The chief's `launch` tool takes an enum of the instances listed when its bundle was built | `harness/src/tools.mjs:116-119` |
|
||||
| E6 | The chief ends a run with `role.revoke` plus a reason (`role.end`). Ending then launching again is already a replace | `broker.mjs:1601-1628`, `tools.mjs:167` |
|
||||
| E7 | `mosaic stop RUN` is a same-uid SIGTERM. It has no bus call and no human proof | `cli.mjs:447-454`, `seat/src/session.mjs:249-270` |
|
||||
| E8 | The Console stops only the assistant (`assistant.stop`) and writes instances through `instances.set` | `serve.mjs:282-289, 315-363, 535` |
|
||||
| E9 | The launcher already starts runs for the human on its 2 s tick: threads with pending messages, and merge holders | `launcher.mjs:133, 487-597` |
|
||||
| E10 | `launch.revoke` and `launch.restore` are human-only. A revoked business refuses `role.launch` | `broker.mjs:770-774, 2087-2095` |
|
||||
| E11 | Only the assistant is END_PROTECTED. Coder, reviewer, pm and cto all declare gitea and vikunja credentials | `vocabulary.mjs:135`, `roles/*.json` |
|
||||
|
||||
The chief's veto today comes from the human-only membership in E2. The chief can only launch what the human
|
||||
listed. It has no way to create an instance, and the human has no way to stop the chief ending a given
|
||||
instance's runs.
|
||||
|
||||
## D1. The verbs
|
||||
|
||||
All of them are human verbs. Each is a broker verb behind `#human(s)` and then the D4 hook. Each logs its
|
||||
actor. The CLI usage joins `INSTANCES_USAGE` (`cli/src/instances.mjs:9`).
|
||||
|
||||
| Verb | CLI | Bus verb | Effect |
|
||||
|---|---|---|---|
|
||||
| V1 create | `business instance add B I --definition DEF [--vars FILE] [--launch] --revision REV` | `instances.set` op `add` | as today, with no restart (D3) |
|
||||
| V2 launch | `business instance launch B I [--wait SECONDS]` | `instance.launch` | one run, through the launcher |
|
||||
| V3 terminate | `business instance terminate B I [--reason TEXT]` | `instance.end` | ends every run of I |
|
||||
| V4 regenerate | `business instance regenerate B I [--wait SECONDS]` | `instance.regenerate` | V3, then V2 once the run has exited |
|
||||
| V5 delete | `business instance delete B I --revision REV` | `instances.set` op `delete` | removes I from the business file |
|
||||
| V6 veto | `business veto B (keep\|unkeep) I` and `business veto B (nohire\|hire) DEF`, each `--revision REV` | `veto.set` | edits `launch.keep` and `launch.noHire` |
|
||||
|
||||
`mosaic business instances B` (read) gains, per instance: `running` (run ids from the bus claims), `kept`
|
||||
and `retired`. The top level gains `noHire`. `mosaic stop RUN` stays as it is. It is the same-uid operator
|
||||
tool, and V3 is the human verb.
|
||||
|
||||
### V2 launch
|
||||
|
||||
1. The broker checks that I exists, isn't a thread instance (`not-thread-launch`: threads start on a
|
||||
message), isn't retired, and that the business isn't revoked (`launch-revoked`). The human's own brake
|
||||
applies to the human until they restore it. When row 191 is present, a halted instance refuses
|
||||
`processing-halted` here, so no launch waits for a resume.
|
||||
2. The broker writes one `action.allowed` event, action `lifecycle.launch`, subject I, actor the human,
|
||||
body `{ request, via }`, where `request` is a generated id. It also writes a `human.input` event, as
|
||||
`launch.revoke` does. The answer is `{ request }`.
|
||||
3. A new launcher pass, `lifecyclePass`, runs on the same tick as `threadPass` and `landPass`. It reads the
|
||||
open requests through a new host op, `lifecycleRequests`. For each request it runs, under `serialize`,
|
||||
`check(I, { human: true })` and then `start({ instance: I, launchedBy: b.human })`.
|
||||
`human: true` lets the human launch an instance that isn't in `launch.instances`. The listing limits
|
||||
the chief, not the human. `launch.by` launches with `pm: true`, as `--pm` does today. Every other step
|
||||
of `check()` and `start()` is unchanged.
|
||||
4. The pass reports the outcome with host op `lifecycleDone`. That op writes `action.allowed`, action
|
||||
`lifecycle.done`, citing the request and naming the run or the refusal code. A request with a done
|
||||
event is closed.
|
||||
5. A request still open 60 s after it was written closes as `request-expired` and never launches. A bus
|
||||
restart therefore can't start runs a human asked for before it.
|
||||
6. The CLI polls a read verb, `instance.request`, until the request closes or `--wait` runs out (default
|
||||
30 s). It prints the run or the refusal. A timeout exits 3 with the request id, and the launch may still
|
||||
happen within the 60 s window.
|
||||
|
||||
The broker never starts a process. The launcher keeps that, as E9 already does for the human's threads.
|
||||
|
||||
### V3 terminate
|
||||
|
||||
1. The broker revokes every claim I holds (`#claimEnd(s, h, 'revoke', null, reason, s.human)`). A thread
|
||||
instance has all its thread claims revoked, which is exactly `assistant.stop`. No claim held answers
|
||||
`not-held`.
|
||||
2. It writes `lifecycle.end` with the request id and the runs. The launcher pass sends SIGTERM to each
|
||||
run's runner after checking its start time, as the idle stop does (`launcher.mjs:526`). The existing
|
||||
exit handler records the end, so the launch journal and `session.ended` are unchanged.
|
||||
3. Terminate is allowed while row 191's switch is on. It starts nothing.
|
||||
|
||||
### V4 regenerate
|
||||
|
||||
A fresh session with the same identity and role. It is V3 then V2 under one request. The pass waits for
|
||||
the old run's exit handler, then runs `check()` and `start()`. The workspace stays, as it does across every
|
||||
launch today. The session is new because every non-thread launch gets a new run dir (`seat/src/session.mjs:107`, newRun).
|
||||
|
||||
A thread instance (the assistant) refuses `regenerate-thread`. Its context lives in the thread's session
|
||||
directory, and clearing that is T1 item 7 (clear context). This row doesn't take it.
|
||||
|
||||
### V5 delete
|
||||
|
||||
`instances.set` gains op `delete`. The writer refuses, in this order:
|
||||
|
||||
| Refusal | When |
|
||||
|---|---|
|
||||
| `organization-definition-refused` | I is END_PROTECTED |
|
||||
| `organization-instance-referenced` | I is `launch.by` or an arbiter. The message names the reference |
|
||||
| `organization-instance-kept` | I is in `launch.keep`. The human runs `veto unkeep` first. Delete never clears a veto silently |
|
||||
| `organization-credentials-out-of-scope` | I holds credentials or a tracker bot. That stays a hand edit until row 195 |
|
||||
| `instance-running` | the broker sees a claim for I. The human terminates first |
|
||||
| BusinessError | I is the last name in `launch.instances` (E1). The message says so |
|
||||
|
||||
Otherwise the writer removes `roles.I` and the references that exist only for I: its `launch.instances`
|
||||
entry, its `launch.limits.tokens.instances` entry and its per-instance model entry. It adds I to a new
|
||||
top-level list, `retired`. The build lists every place `validateBusinessDocument` reads a role instance
|
||||
name and puts each one in one of the two groups above. A reference the build finds that this table doesn't
|
||||
name stops the build and goes back to Webby.
|
||||
|
||||
What stays:
|
||||
|
||||
- Run evidence (`launches/<b>/<run>/`, `launches/<b>.jsonl`) and bus rows are untouched. They are
|
||||
write-once.
|
||||
- The workspace `workspaces/<b>/I/` is renamed to `workspaces/<b>/.retired/I-<transaction>/`, mode 0700.
|
||||
This row deletes no file. A prune with a receipt is a later act.
|
||||
- `retired` stops a later create from reusing the name. Decisions bind to the role instance and outlive
|
||||
restarts (`broker.mjs:783-788`). A new `coder` would otherwise inherit an approval raised by the old one.
|
||||
The tradeoff is a name that can't come back without a hand edit. I chose that over voiding decisions,
|
||||
which would touch a second store.
|
||||
|
||||
### V6 veto
|
||||
|
||||
`veto.set` args: `{ revision, op: "keep" | "unkeep" | "nohire" | "hire", name }`. It goes through the
|
||||
organization writer, so it is logged with actor and diff in `changes.jsonl`. `keep` needs a declared,
|
||||
non-END_PROTECTED instance. `nohire` needs a definition in `rolesDir`. A no-op answers
|
||||
`organization-no-change`. It is human-only and isn't in `TESTER_ORGANIZATION_VERBS` (Q3).
|
||||
|
||||
## D2. Chief hire and fire
|
||||
|
||||
### D2.1 What the chief may do
|
||||
|
||||
| Act | Bus verb | Rule |
|
||||
|---|---|---|
|
||||
| launch | `launch` (host) | as today: listed instances only |
|
||||
| end a run | `role.revoke` + reason | as today, and refused `instance-kept` when the target is kept |
|
||||
| hire | `instance.hire { name, definition, vars? }` | adds and lists a new instance. Refused when the definition is in `noHire`, END_PROTECTED or credentialed, or the name exists or is retired |
|
||||
| fire | `instance.fire { name, reason }` | ends I's runs and removes I from `launch.instances`. Refused when I is kept, END_PROTECTED, `launch.by` or the last listed instance |
|
||||
|
||||
The chief never deletes. A fired instance stays in `roles` and can be listed again by a hire of the same
|
||||
name (that is a `change` with `launch: true`, so no new name is spent) or by the human. Delete stays a
|
||||
human act because it retires the name.
|
||||
|
||||
Hire and fire need authority the chief doesn't have. That is Q1. The two options:
|
||||
|
||||
- O1, recommended. Add `role.hire` and `role.fire` to `ACTIONS` (`vocabulary.mjs:11`) and to
|
||||
`roles/chief-of-staff.json` withinRole. The broker checks them with `#consumeAuthority`, as it does
|
||||
`role.end`. This is a role file change (AGENTS.md invariant 7) and widens what a chief can do. It is what
|
||||
Case 1 asks for ("hire/fire authority for agents as needed; unless user forbids per agent").
|
||||
- O2. No role file change. The chief raises a gated `role.hire` or `role.fire` decision, and the human
|
||||
resolves it. This follows the brief's "verbs, not new role authority" literally, but Case 1's authority
|
||||
then sits with the human.
|
||||
|
||||
The brief says this row adds verbs, not authority. Case 1 says the chief hires and fires. Both can't hold,
|
||||
so the choice is Sage's and Mos's. The rest of this design works under either option. Only the broker's
|
||||
authority check and the role file differ.
|
||||
|
||||
### D2.2 The veto
|
||||
|
||||
- `launch.keep`: instance names. The broker refuses every chief act aimed at a kept instance except launch:
|
||||
`role.end` (E6 shows end then launch is a replace), fire, and a hire that would change it. The chief
|
||||
can't stop a runaway kept instance. The human and the limits still can. I chose the wider rule because
|
||||
"can't fire or replace it" is otherwise open to end-then-relaunch.
|
||||
- `launch.noHire`: definition names. `instance.hire` refuses `definition-not-hireable`.
|
||||
- Default, per Jason's wording "unless user forbids per agent": everything the structural rules allow is
|
||||
hireable and nothing is kept. The structural rules are uncredentialed, not END_PROTECTED, and a valid
|
||||
version 2 role.
|
||||
|
||||
### D2.3 Where it lives and how the broker enforces it
|
||||
|
||||
The veto lives in the business file, under `launch`, beside the membership it limits:
|
||||
`launch.keep: [instance]`, `launch.noHire: [definition]`, and top-level `retired: [instance]`. That file is
|
||||
the human's. The chief reaches it only through `organization.set`, whose keys (`ORGANIZATION_KEYS`,
|
||||
`broker.mjs:145`) exclude `launch` and `retired`. A test pins that (T13).
|
||||
|
||||
`checkLaunch` (`business.mjs:118`) gains the two keys. Each list is distinct. `keep` names declared,
|
||||
non-protected instances. `noHire` names definitions present in `rolesDir`. `retired` names none of
|
||||
`roles`. This is the business-file schema change for review. Old files without the keys load unchanged. An
|
||||
old bus loader rejects a file that has them (`rejectUnknownKeys`), so a rollback past this row needs the
|
||||
keys removed first. The activation plan has to say so (R4).
|
||||
|
||||
The broker enforces from its live copy of the business, which D3 keeps current. The checks sit in the
|
||||
`role.revoke` reason branch and in the two new chief verbs, before any write.
|
||||
|
||||
## D3. Live changes
|
||||
|
||||
Today the broker's `#businesses` is fixed at start (E3), and `instances.set` answers `restart: true` for
|
||||
add and redefine.
|
||||
|
||||
1. `Broker.reloadBusiness(id, document)` swaps in one business's validated document. It is synchronous, so
|
||||
no request sees half of it. It refuses an unknown id and a document whose `human` changed (that is row
|
||||
193's ground).
|
||||
2. The runtime's organization callback (`runtime.mjs:121-133`) calls it after every committed write by
|
||||
`instances.set`, `veto.set`, `instance.hire`, `instance.fire`, `limits.set` and `organization.set`. It
|
||||
loads from the file through the same loader `bootConfig` uses (`cli/src/config.mjs:88-105`), so the
|
||||
broker holds exactly what a restart would give it. Then `limits.refresh(id)` runs, as `plugins.set` does
|
||||
now.
|
||||
3. Other startup consumers of `businesses` in `startBroker` (`runtime.mjs:75-81`): credential references,
|
||||
usage, the web services and the tasks adapter's bots. A create or hire adds no credentials, no tracker
|
||||
bot and no web grant, because credentialed definitions are refused. The build checks each consumer
|
||||
against that claim. Any change that would need one of them still answers `restart: true` and doesn't
|
||||
reload.
|
||||
4. A definition change on a running instance refuses `instance-running`. A live cap must not change
|
||||
authority under its run. This is new: today the change waits for the restart.
|
||||
5. The chief's `launch` tool (E5) takes `instance` as a string with the id pattern instead of the start-time
|
||||
enum. Its description says the host refuses an unlisted instance. `check()` already refuses one
|
||||
(`launcher.mjs:235`). Without this, the chief can't launch its own hire until its session restarts.
|
||||
`tools.mjs` may carry hash pins in tests. The build greps for them (R5).
|
||||
6. Hand edits of the business file still need a restart. Nothing watches the file.
|
||||
|
||||
After this, `restart` is false for create, delete, hire, fire and veto changes, and true only under item 3.
|
||||
|
||||
## D4. RBAC until row 193
|
||||
|
||||
Every human verb above calls `#human(s)` and then one hook, `#mayHuman(s, verb, target)`. Until row 193
|
||||
lands, the hook passes for the business's one `human`, which is the owner. The verbs are listed once, in
|
||||
an exported `LIFECYCLE_HUMAN_VERBS` (`instance.launch`, `instance.end`, `instance.regenerate`, `veto.set`,
|
||||
and `instances.set` ops `add`, `change`, `delete`), so row 193 maps each verb to the human roles that may
|
||||
use it. Row 193 replaces the hook body and nothing else. The tester gets none of the new write verbs (Q3).
|
||||
|
||||
## D5. One stack instance
|
||||
|
||||
Row 190 carries the stack instance as the config `MOSAIC_CONFIG` names. These verbs take no instance name.
|
||||
The CLI reaches the broker socket under that config's `dataRoot`. The broker acts on its own businesses,
|
||||
launcher and data root. A business id that exists in two stack instances is two separate businesses. A test
|
||||
runs two scratch roots with the same business id and shows a launch, a terminate and a delete in one leave
|
||||
the other byte-identical (T11).
|
||||
|
||||
## D6. The kill switch
|
||||
|
||||
- Launch and regenerate end in `start()`, which row 191 gates (its D3 inventory, `launcher.mjs:329-457`).
|
||||
The new pass adds no second start path.
|
||||
- The broker refuses `instance.launch` and `instance.regenerate` with `processing-halted` while the switch
|
||||
is on (V2 step 1), so no request is waiting when a human resumes.
|
||||
- `instance.hire` and `instance.fire` are agent work verbs, so row 191's default refuses them while halted.
|
||||
The build adds them to row 191's work table, not its read list.
|
||||
- Terminate, create, delete and veto start nothing and stay allowed.
|
||||
- If row 191 hasn't landed when this row builds, the build chains on its candidate (Q177). The two halted
|
||||
checks are written against its reader and tested with its fixture.
|
||||
|
||||
## D7. The Console
|
||||
|
||||
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:
|
||||
|
||||
- Create (definition select, from `definitions`), Delete (with a confirm naming what stays: the evidence
|
||||
and the workspace under `.retired`).
|
||||
- Launch, Terminate (with an optional reason), Regenerate (with a confirm).
|
||||
- Keep and Unkeep per instance, and a No-hire toggle per definition.
|
||||
|
||||
The routes are `POST /api/instances` (op `delete` joins `add` and `change`), `POST /api/agent/launch`,
|
||||
`/api/agent/terminate`, `/api/agent/regenerate` and `POST /api/veto`. Each uses `instanceSend`'s boundary:
|
||||
signed-in user, exact Origin, same-origin Fetch Metadata, JSON within 16 KiB, strict keys, one bus call
|
||||
through the human transport, and `outcome-unknown` on an unexpected answer. Launch and regenerate show the
|
||||
request, then poll `GET /api/agent/request?id=` until it closes.
|
||||
|
||||
When `/api/config` says `send` is false, every action button shows as unavailable with the text "Agent
|
||||
actions are unavailable here. Use mosaic business instance from the instance CLI." This is the live state
|
||||
until Q175 phase 2. Every write test uses a fixture server and fixture writers, as rows 165 and 191 do. No
|
||||
route gets a `--send` exception.
|
||||
|
||||
## D8. Tests
|
||||
|
||||
Every test uses scratch roots under `TMPDIR` and the existing fixtures (`business/tests/helpers.mjs`,
|
||||
`cli/tests/helpers.mjs`, `webui/tests/tester-fixture.mjs`). None touches `~/.mosaic-dev`,
|
||||
`~/.config/mosaic-dev`, a live unit or a slot file.
|
||||
|
||||
| Test | Shows | File |
|
||||
|---|---|---|
|
||||
| T1 | V1 create on a running bus: a launch of the new instance binds with no restart, and the answer says `restart: false` | cli `instances.test.mjs` |
|
||||
| T2 | V2: a human launch of an unlisted instance runs. A thread instance, a retired one and a revoked business refuse. A request older than 60 s never launches. A refused `check()` closes the request with its code | cli `lifecycle.test.mjs` (new) |
|
||||
| T3 | V2 with `--no-launch` and with each limit refusal: no process, request closed with the code | same |
|
||||
| T4 | V3: claims revoked, runner SIGTERMed after the start-time check, one end record. A mismatched start time sends no signal | same |
|
||||
| T5 | V4: the new run starts only after the old exit, with a new run dir and the same workspace. The assistant refuses `regenerate-thread` | same |
|
||||
| T6 | V5: each refusal in the V5 table, in order. A clean delete leaves the evidence bytes unchanged, renames the workspace, adds `retired`, and a create of the same name refuses | business `instance-settings.test.mjs` |
|
||||
| T7 | V6: each op, the no-op refusal, and a `changes.jsonl` entry with actor `human` | business |
|
||||
| T8 | Chief hire: added and listed, then launched by the same chief session (D3 item 5). `noHire`, credentialed, END_PROTECTED, existing and retired each refuse | bus `chief-of-staff.test.mjs` |
|
||||
| T9 | Chief fire: runs end, delisted, still in `roles`. Kept, `launch.by` and last-listed refuse | same |
|
||||
| T10 | Keep: chief `role.end`, fire and hire aimed at a kept instance refuse `instance-kept`. The human's V3 on it 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 | cli `lifecycle.test.mjs` |
|
||||
| T12 | Agent, reader and tester sessions refuse every human verb before any effect. The D4 hook is called with the verb | bus `instances-verb.test.mjs` |
|
||||
| T13 | `organization.set` with `launch`, `retired`, `keep` or `noHire` keys refuses | bus |
|
||||
| T14 | Live reload: after each write the broker answers from the new document. A definition change on a running instance refuses `instance-running` | bus |
|
||||
| T15 | Kill switch (with row 191's fixture): launch and regenerate refuse `processing-halted`, hire and fire refuse, terminate works | bus and cli |
|
||||
| T16 | Console: each route's boundary refusals, a fixture write per action, `outcome-unknown` on a bad shape, unavailable buttons when `send` is false, request polling | webui `agents.test.mjs` (new) |
|
||||
| T17 | Business file: the new keys validate, each bad entry refuses, and a file without them loads byte for byte as before | business `business.test.mjs` |
|
||||
|
||||
### Mutants
|
||||
|
||||
Each one must fail at least one test. They run against the candidate, and the file is restored and its
|
||||
sha256 checked after each one.
|
||||
|
||||
| # | Change | Killer |
|
||||
|---|---|---|
|
||||
| M1 | `instance.launch` skips `#human` | T12 |
|
||||
| M2 | the pass calls `check()` without `human: true` | T2 |
|
||||
| M3 | the pass starts a request older than 60 s | T2 |
|
||||
| M4 | the revoked-business check is skipped for the human | T2 |
|
||||
| M5 | terminate signals without the start-time check | T4 |
|
||||
| M6 | regenerate starts before the old exit | T5 |
|
||||
| M7 | delete skips the running check | T6 |
|
||||
| M8 | delete skips `retired` | T6 |
|
||||
| M9 | delete clears `keep` instead of refusing | T6 |
|
||||
| M10 | delete removes the workspace instead of renaming it | T6 |
|
||||
| M11 | the `keep` check is removed from `role.end` | T10 |
|
||||
| M12 | `noHire` is not read by hire | T8 |
|
||||
| M13 | fire deletes the instance from `roles` | T9 |
|
||||
| M14 | `reloadBusiness` isn't called after `instances.set` | T1, T14 |
|
||||
| M15 | the tool enum is kept | T8 |
|
||||
| M16 | the halted check is dropped from `instance.launch` | T15 |
|
||||
| M17 | the Console route skips the Origin check | T16 |
|
||||
| M18 | `veto.set` joins `TESTER_ORGANIZATION_VERBS` | T12 |
|
||||
|
||||
## Scope and boundaries
|
||||
|
||||
- In the row: the verbs and passes above in `business` (instance-settings, business validation), `bus`
|
||||
(broker verbs, reload, runtime callback), `cli` (instances command, launcher pass, host ops), `harness`
|
||||
(the launch tool's schema, and the hire and fire tools under O1), `webui` (routes and the Agents
|
||||
section), the tests, and the package READMEs. `docs/TOOLS.md` text goes to Sage.
|
||||
- Role file change: `roles/chief-of-staff.json` under O1 only. Business file schema: D2.3. Both are named
|
||||
for review.
|
||||
- Not in the row: credentialed hires and deletes (row 195), clear context for the assistant (T1 item 7),
|
||||
human roles (row 193), and any live activation or Console start.
|
||||
|
||||
## Risks
|
||||
|
||||
- R1. Case 1's working roles (coder, reviewer, pm, cto) all declare credentials (E11). Neither the human
|
||||
verb nor the chief can create or delete them until row 195 binds credentials. Until then this row covers
|
||||
uncredentialed roles only. That is the largest gap against Case 1, and it isn't this 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
|
||||
`human-cli.mjs` can launch and delete. Row 190b's separate uid and row 193 narrow it. This row adds no
|
||||
check of its own.
|
||||
- R3. The 60 s request window means a slow `check()` (checkouts) can still start a run up to that point
|
||||
after the CLI gave up. The CLI says so on timeout.
|
||||
- R4. Rollback past this row needs `keep`, `noHire` and `retired` removed from each business file, or the
|
||||
old loader refuses it. The activation plan states this.
|
||||
- R5. `tools.mjs`, `serve.mjs` and `broker.mjs` may carry sha256 pins in other packages' tests. The build
|
||||
greps the tests for every changed path plus a 64-hex value before in-review.
|
||||
|
||||
## Questions
|
||||
|
||||
- Q1, for Sage and Mos: chief hire and fire authority, O1 (role file change) or O2 (gated decisions)?
|
||||
D2.1. I recommend O1.
|
||||
- Q2, for Webby: keep blocks the chief's `role.end` on that instance, not only fire and replace (D2.2). Is
|
||||
the wider rule right?
|
||||
- Q3, for Sage: the tester gets no lifecycle write verbs. The Q173 tester could then test the Agents section
|
||||
only through fixtures. Should launch and terminate be tester verbs on the tester's own scratch business?
|
||||
Reference in new issue
Block a user