M2.8, the switch-over (end of Phase 2). nisse-dev now runs only the conversational loop. The gated state machine, and everything that existed only for it, is gone. The diff is +~920 / −~8,600.
Removed
Source:control.py, planner.py, executor.py, approvals.py, publish.py, poller.py and gates.py. command_form moves to loop/reader.py.
The old assembly (agent.py): its shared helpers move into loop/build.py, each now taking an explicit project.
The store's state-machine view:get_state, transition and AgentState.
Specs:specs/orchestrator.qnt and its scenarios. loop.qnt and the property walk remain.
Old-path tests: 15 unit test files, plus the old end-to-end test and the live planning, milestone and gate tests.
Configuration
Roles are driver, helper, expert and classifier, and driver is required.
loop:, roles.planner, roles.executor and tuning.executor are refused with what to use instead. For example: roles.planner: retired — use roles.driver.
tuning.driver takes the turn cap and the failure limit.
nisse-dev.example.yaml, nisse-test.yaml and the Tuning Guide are updated. The bring-up config on this box is updated too, with a backup kept as nisse-dev.yaml.pre-m2.8.
Everywhere on the new loop (your choice on the bench)
The benchmark measures the Driver. It uses the loop's own system prompt and tools, with the task's plan given as the agreed restatement. Delegation and ask_expert stay off.
Task plans now end with finish_milestone, so earlier runs show as (old). Stored runs' executor tuning reads as driver, so old rows aren't all flagged as off-default.
The capability matrix checks the restatement contract (restate/ask_human) instead of submit_plan, and finish_milestone instead of finish.
Found on the way: the new loop's delegations didn't record what the helper's alias resolved to (L9 eval history). The old path did. This is fixed (Helpers.backing) and has a test.
Coverage kept: I checked each removed test file for behaviour the loop shares, and ported what was missing:
Driver runs:
a failed model call fails the run;
a stop mid-turn skips the rest of the turn's calls;
a command that works resets the failure count.
Go-aheads ignore case and trailing punctuation (#64).
A request too big to fit is never sent.
The dump shows an item's revision and what it waits for.
The oplog, state, worker, crash-durability and dump tests now use the work-item API.
The assembly tests now run against build_loop_agent: graph, routes, tuning, model discovery, projects.
Tests, run locally before pushing:
Unit: 562 passed.
Integration: 17 passed, plus 7 opt-in skips.
Capability matrix: 14 passed on claude-haiku-4-5 and qwen3.8-27b-nothink.
Full e2e: 8 of 8 passed (11m31s).
This is a milestone PR, so it carries e2e:full.
Docs: the Development Plan marks M2.8 done and updates the status table. The Tuning Guide documents the driver knobs. specs/README.md drops the orchestrator model.
**M2.8, the switch-over (end of Phase 2).** nisse-dev now runs only the conversational loop. The gated state machine, and everything that existed only for it, is gone. The diff is +~920 / −~8,600.
**Removed**
- **Source:** `control.py`, `planner.py`, `executor.py`, `approvals.py`, `publish.py`, `poller.py` and `gates.py`. `command_form` moves to `loop/reader.py`.
- **The old assembly** (`agent.py`): its shared helpers move into `loop/build.py`, each now taking an explicit project.
- **The store's state-machine view:** `get_state`, `transition` and `AgentState`.
- **Specs:** `specs/orchestrator.qnt` and its scenarios. `loop.qnt` and the property walk remain.
- **Old-path tests:** 15 unit test files, plus the old end-to-end test and the live planning, milestone and gate tests.
**Configuration**
- Roles are `driver`, `helper`, `expert` and `classifier`, and `driver` is required.
- `loop:`, `roles.planner`, `roles.executor` and `tuning.executor` are refused with what to use instead. For example: `roles.planner: retired — use roles.driver`.
- `tuning.driver` takes the turn cap and the failure limit.
- `nisse-dev.example.yaml`, `nisse-test.yaml` and the Tuning Guide are updated. The bring-up config on this box is updated too, with a backup kept as `nisse-dev.yaml.pre-m2.8`.
**Everywhere on the new loop (your choice on the bench)**
- **The benchmark measures the `Driver`.** It uses the loop's own system prompt and tools, with the task's plan given as the agreed restatement. Delegation and `ask_expert` stay off.
- **Task plans** now end with `finish_milestone`, so earlier runs show as `(old)`. Stored runs' `executor` tuning reads as `driver`, so old rows aren't all flagged as off-default.
- **The capability matrix** checks the restatement contract (`restate`/`ask_human`) instead of `submit_plan`, and `finish_milestone` instead of `finish`.
**Found on the way:** the new loop's delegations didn't record what the helper's alias resolved to (L9 eval history). The old path did. This is fixed (`Helpers.backing`) and has a test.
**Coverage kept:** I checked each removed test file for behaviour the loop shares, and ported what was missing:
- Driver runs:
- a failed model call fails the run;
- a stop mid-turn skips the rest of the turn's calls;
- a command that works resets the failure count.
- Go-aheads ignore case and trailing punctuation (#64).
- A request too big to fit is never sent.
- The dump shows an item's revision and what it waits for.
- The oplog, state, worker, crash-durability and dump tests now use the work-item API.
- The assembly tests now run against `build_loop_agent`: graph, routes, tuning, model discovery, projects.
**Tests, run locally before pushing:**
- **Unit:** 562 passed.
- **Integration:** 17 passed, plus 7 opt-in skips.
- **Capability matrix:** 14 passed on `claude-haiku-4-5` and `qwen3.8-27b-nothink`.
- **Full e2e:** 8 of 8 passed (11m31s).
This is a milestone PR, so it carries `e2e:full`.
**Docs:** the Development Plan marks M2.8 done and updates the status table. The Tuning Guide documents the driver knobs. `specs/README.md` drops the orchestrator model.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
nisse-dev now runs only the conversational loop. The gated state machine
it was built alongside is gone:
- Removed control.py, planner.py, executor.py, approvals.py, gates.py
(command_form moves to loop/reader.py), publish.py and poller.py, the
old assembly in agent.py (its shared helpers move to loop/build.py),
the store's state-machine view (get_state, transition), their tests,
the old end-to-end and live planning/milestone/gate tests, and
specs/orchestrator.qnt with its scenarios.
- Roles are driver, helper, expert and classifier; driver is required.
loop:, roles.planner, roles.executor and tuning.executor are refused
with what to use instead; tuning.driver takes the turn cap and failure
limit.
- The benchmark and the capability matrix measure the driver: the
loop's own system prompt and tools, the task's plan as the agreed
restatement. Task plans end with finish_milestone, so earlier runs
show as (old); stored runs' executor tuning reads as the driver's.
- A delegation on the loop records what its helper's alias resolved to
(L9), as the old path's did.
- The dump shows a work item's revision and what it waits for.
- Behaviour the old tests covered and the loop shares stays covered:
driver runs that fail on a model call, stop mid-turn, reset the
failure count; go-aheads ignoring case and punctuation; a request too
big to fit is never sent; the assembly tests, now on build_loop_agent.
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.
M2.8, the switch-over (end of Phase 2). nisse-dev now runs only the conversational loop. The gated state machine, and everything that existed only for it, is gone. The diff is +~920 / −~8,600.
Removed
control.py,planner.py,executor.py,approvals.py,publish.py,poller.pyandgates.py.command_formmoves toloop/reader.py.agent.py): its shared helpers move intoloop/build.py, each now taking an explicit project.get_state,transitionandAgentState.specs/orchestrator.qntand its scenarios.loop.qntand the property walk remain.Configuration
driver,helper,expertandclassifier, anddriveris required.loop:,roles.planner,roles.executorandtuning.executorare refused with what to use instead. For example:roles.planner: retired — use roles.driver.tuning.drivertakes the turn cap and the failure limit.nisse-dev.example.yaml,nisse-test.yamland the Tuning Guide are updated. The bring-up config on this box is updated too, with a backup kept asnisse-dev.yaml.pre-m2.8.Everywhere on the new loop (your choice on the bench)
Driver. It uses the loop's own system prompt and tools, with the task's plan given as the agreed restatement. Delegation andask_expertstay off.finish_milestone, so earlier runs show as(old). Stored runs'executortuning reads asdriver, so old rows aren't all flagged as off-default.restate/ask_human) instead ofsubmit_plan, andfinish_milestoneinstead offinish.Found on the way: the new loop's delegations didn't record what the helper's alias resolved to (L9 eval history). The old path did. This is fixed (
Helpers.backing) and has a test.Coverage kept: I checked each removed test file for behaviour the loop shares, and ported what was missing:
build_loop_agent: graph, routes, tuning, model discovery, projects.Tests, run locally before pushing:
claude-haiku-4-5andqwen3.8-27b-nothink.This is a milestone PR, so it carries
e2e:full.Docs: the Development Plan marks M2.8 done and updates the status table. The Tuning Guide documents the driver knobs.
specs/README.mddrops the orchestrator model.🤖 Generated with Claude Code
https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE