Development Plan M1.1, as discussed: work items in the state store, a transcript bound to one work item and one actor, a fresh v4 schema, and the role renames. Two commits: the rename first, then the work items.
1. Roles renamed (L9): delegate → helper, consultant → expert, in RoleMap, config keys, recorded events and tests. planner and executor stay distinct until M2.3 merges them into driver. Configs need the new keys; the example and test configs are updated here, and so is this box's bring-up config.
2. Work items (L1, L7)
Schema v4: a work_items table (id, scope, title, status, outcome, position, data) and an agent row whose active_item points at one; NULL means idle.
Canonical events carry work_item and actor, and intents carry work_item.
Older stores are refused with a clear message: there's no production data, so no migration. The migration mechanism stays; synthetic-version tests still exercise it.
store.transcript(item, actor) returns a view that reads only that work item and writes with both.
The planner, executor, delegation and expert desk receive one per call from the orchestrator, and no longer hold the store. A component therefore can't read another task's events (#78's planner read all of them) or write without attribution.
Delegation keeps the store only for boot recovery, and finds an interrupted delegation's work item from its intent.
store.all_events() remains for readers that span items: evaluation capture and dumps.
The orchestrator:
opens a work item on use, and closes it with its outcome (completed or abandoned);
carries what the operator said while idle into the new item, as copies noting where they came from (the log is never edited). So "task first, then use", run 2's flow, still reaches the planner (D1).
control.py keeps its state machine through get_state() / transition(), now backed by the active work item, until M2.6 retires it.
Expert consult budget is counted per work item; there's a new test for it.
nisse_dev.dump shows the work items, plus each event's item and actor.
Tests
test_work_items.py (10): transcript scoping, attribution, an unknown actor refused, intent ownership, items and the active pointer surviving a restart, outcomes, idle messages carried with their origin, and at the orchestrator level: a second task never sees the first, each task its own item with its outcome, and a task sent before use reaching the planner.
The existing suites now pass a transcript per call, or read through all_events() where they check across items. The oplog literal-format test now documents the work_item.open / work_item.switch lines.
Results: 514 unit tests and the integration suite (19 passed, 7 skipped) pass; lint and format clean; Quint scenarios pass. The model doesn't change, since the loop's behaviour doesn't.
Smoke run:scripts/bringup.sh start booted the agent on a fresh v4 store, with the v3 store set aside.
Not in this PR (per the plan): parking and switching between items (the schema allows it), and per-role views with fact events, which are M1.2.
Development Plan **M1.1**, as discussed: work items in the state store, a transcript bound to one work item and one actor, a fresh v4 schema, and the role renames. Two commits: the rename first, then the work items.
**1. Roles renamed** (L9): `delegate` → `helper`, `consultant` → `expert`, in `RoleMap`, config keys, recorded events and tests. `planner` and `executor` stay distinct until M2.3 merges them into `driver`. Configs need the new keys; the example and test configs are updated here, and so is this box's bring-up config.
**2. Work items** (L1, L7)
- **Schema v4:** a `work_items` table (id, scope, title, status, outcome, position, data) and an `agent` row whose `active_item` points at one; `NULL` means idle.
- Canonical events carry `work_item` and `actor`, and intents carry `work_item`.
- **Older stores are refused** with a clear message: there's no production data, so no migration. The migration mechanism stays; synthetic-version tests still exercise it.
- **`store.transcript(item, actor)`** returns a view that reads only that work item and writes with both.
- The planner, executor, delegation and expert desk **receive one per call** from the orchestrator, and no longer hold the store. A component therefore can't read another task's events (#78's planner read all of them) or write without attribution.
- Delegation keeps the store only for boot recovery, and finds an interrupted delegation's work item from its intent.
- `store.all_events()` remains for readers that span items: evaluation capture and dumps.
- **The orchestrator:**
- opens a work item on `use`, and closes it with its outcome (**completed** or **abandoned**);
- carries what the operator said while idle into the new item, as copies noting where they came from (the log is never edited). So "task first, then `use`", run 2's flow, still reaches the planner (D1).
- `control.py` keeps its state machine through `get_state()` / `transition()`, now backed by the active work item, until M2.6 retires it.
- **Expert consult budget** is counted per work item; there's a new test for it.
- **`nisse_dev.dump`** shows the work items, plus each event's item and actor.
**Tests**
- **`test_work_items.py`** (10): transcript scoping, attribution, an unknown actor refused, intent ownership, items and the active pointer surviving a restart, outcomes, idle messages carried with their origin, and at the orchestrator level: **a second task never sees the first**, each task its own item with its outcome, and a task sent before `use` reaching the planner.
- **The existing suites** now pass a transcript per call, or read through `all_events()` where they check across items. The oplog literal-format test now documents the `work_item.open` / `work_item.switch` lines.
- **Results:** 514 unit tests and the integration suite (19 passed, 7 skipped) pass; lint and format clean; Quint scenarios pass. The model doesn't change, since the loop's behaviour doesn't.
- **Smoke run:** `scripts/bringup.sh start` booted the agent on a fresh v4 store, with the v3 store set aside.
**Not in this PR** (per the plan): parking and switching between items (the schema allows it), and per-role views with `fact` events, which are M1.2.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Architecture L9's role names, for the two roles with a direct
counterpart: the delegation role is now 'helper' and the ask_expert role
'expert', in RoleMap, configuration (roles.helper, roles.expert),
recorded events and tests. 'planner' and 'executor' stay distinct for
now; they become 'driver' when the new loop merges them (Development
Plan M2.3).
Configurations must rename their keys: nisse-dev.example.yaml and
tests/integration/nisse-test.yaml are updated here.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Development Plan M1.1, as discussed.
- Schema v4: a work_items table (id, scope, title, status, outcome,
position, data) and an agent row whose active_item points at one
(NULL: idle). Canonical events and intents carry their work item, and
events their actor. Older stores are refused with a clear message:
there was no production data, so no migration (the mechanism stays).
- The log is reached through a Transcript bound to one work item and
one actor: store.transcript(item, actor) reads only that item and
writes with both. The planner, executor, delegation and expert desk
get one per call from the orchestrator instead of holding the store,
so a component can neither read another task's events (the planner
read every earlier task's, #78) nor write without attribution.
store.all_events() is kept for readers that span items (evaluation
capture, dumps). Intents are begun through the transcript or with
work_item=, so each belongs to its item.
- The orchestrator opens a work item on 'use', closes it with its
outcome (completed or abandoned), and carries what the operator said
while idle into the new item as copies with their origin, so a task
sent before 'use' is still planned from (D1). control.py keeps its
state machine through get_state()/transition(), now over the active
work item, until M2.6 retires it.
- nisse_dev.dump shows work items and each event's item and actor.
Tests: test_work_items.py (scoping, attribution, intent ownership,
restart, outcomes, carried messages, a second task never seeing the
first); the suites updated for the per-call transcript.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Development Plan M1.1, as discussed: work items in the state store, a transcript bound to one work item and one actor, a fresh v4 schema, and the role renames. Two commits: the rename first, then the work items.
1. Roles renamed (L9):
delegate→helper,consultant→expert, inRoleMap, config keys, recorded events and tests.plannerandexecutorstay distinct until M2.3 merges them intodriver. Configs need the new keys; the example and test configs are updated here, and so is this box's bring-up config.2. Work items (L1, L7)
work_itemstable (id, scope, title, status, outcome, position, data) and anagentrow whoseactive_itempoints at one;NULLmeans idle.work_itemandactor, and intents carrywork_item.store.transcript(item, actor)returns a view that reads only that work item and writes with both.store.all_events()remains for readers that span items: evaluation capture and dumps.use, and closes it with its outcome (completed or abandoned);use", run 2's flow, still reaches the planner (D1).control.pykeeps its state machine throughget_state()/transition(), now backed by the active work item, until M2.6 retires it.nisse_dev.dumpshows the work items, plus each event's item and actor.Tests
test_work_items.py(10): transcript scoping, attribution, an unknown actor refused, intent ownership, items and the active pointer surviving a restart, outcomes, idle messages carried with their origin, and at the orchestrator level: a second task never sees the first, each task its own item with its outcome, and a task sent beforeusereaching the planner.all_events()where they check across items. The oplog literal-format test now documents thework_item.open/work_item.switchlines.scripts/bringup.sh startbooted the agent on a fresh v4 store, with the v3 store set aside.Not in this PR (per the plan): parking and switching between items (the schema allows it), and per-role views with
factevents, which are M1.2.🤖 Generated with Claude Code
https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE