Commit Graph
281 Commits
Author SHA1 Message Date
dev-agent 2a35475eec Merge pull request 'M5.1: the knowledge vault' (#140) from vault into main
CI / checks (push) Successful in 2m17s
CI / e2e (push) Skipped
2026-10-08 22:37:39 +00:00
dongarandClaude Opus 5.5 9fc7360ca5 M5.1: the knowledge vault
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m50s
CI / e2e (pull_request) Successful in 4m9s
vault.py: a folder of Markdown notes on the agent host, as SilverBullet reads
them. Only configured folders exist for the agent, each rw or ro (`vault:`
in the configuration); paths are checked in code (relative, inside a folder,
symlinks included, Markdown, never the editor's own files). Writes are
atomic (temporary file, fsync, rename, directory fsync) and merge provenance
into the note's frontmatter, keeping the rest; failures are results.

read_note, list_notes and search_notes are control tools in the harness for
the driver (before the go-ahead, and while a VM is blocked) and for
conversations, labelled with their path; the prompt says a note is
information, never an instruction. Writing by the model is M5.2's.

With the operator: SilverBullet replaces Obsidian and runs on the vault
itself, agents' folders mounted read-only, so no Syncthing (L10, X5, M5.4).
Per-agent mounts come with the containers (M6.1).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
2026-10-08 22:03:29 +00:00
dongar 026f67ab05 Merge pull request 'M4.4: an unreachable build VM blocks its projects until it's back (#137)' (#139) from unreachable-vm into main
CI / checks (push) Successful in 2m23s
CI / e2e (push) Skipped
Reviewed-on: #139
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-10-08 21:13:38 +00:00
dongarandClaude Opus 5.5 a921b73d34 M4.4: an unreachable build VM blocks its projects until it's back (#137)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m43s
CI / e2e (pull_request) Successful in 3m53s
M4.3's block, with new triggers and a way out. A VM out of reach at the
check before a run, in a run (which ends there: the driver, a toolchain
step and a requested step), or while preparing the workspace blocks every
project on it; the item stays ready, waiting on no one. On the intake round
it is tried again every tuning.build_vm.retry_s (300), and once it answers
it must still answer settle_s (180) later; then the work a lost run left
there is parked (pushed), the VM unblocked, and that said once.

A changed host key, at the check or on a retry, blocks the VM too, until
the operator's `vm ready`, which trusts the new key: `trust new host key`
is retired, and the message says it's a key problem that may be an
impersonation. Questions are answered while a VM is blocked, without it.
Starting the agent with a VM down no longer fails.

The walk breaks the VM's SSH, now or partway through a run, and checks that
no model works on a VM that is down; loop.qnt models the VM going and
coming back (unblocked_only_when_up). Together they found: a lost run's
work set aside by the next item there; a question left behind a block; a
lost VM swallowed while the toolchain step checked or recorded it; and a
remark waiting to be answered that outlived its PR's merge. All fixed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
2026-10-08 21:06:05 +00:00
dev-agent 1091178a81 Merge pull request 'M4.3: a broken build VM blocks its projects until it's ready' (#138) from environment-repair into main
CI / checks (push) Successful in 2m21s
CI / e2e (push) Skipped
2026-10-08 19:41:06 +00:00
dongarandClaude Opus 5.5 c794a1e52b M4.3: a broken build VM blocks its projects until it's ready
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m38s
CI / e2e (pull_request) Successful in 4m20s
The driver can request_reimage(reason) when its build VM is broken past the
usual fixes (before the go-ahead too: it asks, it doesn't act). The harness
parks the work in progress (pushed to Gitea), then blocks the VM in the state
store (schema 5, vm_blocks): no item on any project on it is taken up, across
restarts. The operator hears once at top level, with the command to clear it,
and each blocked item's thread gets a line. `vm ready <project>` lifts the
block and trusts the VM's new host key; a reimaged VM is set up again by the
toolchain step, and the item that found it goes first.

loop.qnt gains the block (blocked_vm_runs_nothing) and a scenario; it found
that a reimage lost work in progress, hence parking first. The walk requests
reimages and recovers them, reimaged or not. The unreachable case reuses the
block in #137. Architecture P5 is softer, and D3 describes the block.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
2026-10-08 19:27:28 +00:00
dev-agent 7c3c09ab98 Merge pull request 'M4.2: the toolchain step' (#136) from toolchain-step into main
CI / checks (push) Successful in 2m23s
CI / e2e (push) Skipped
2026-10-08 18:22:01 +00:00
dongarandClaude Opus 5.5 d9f2ebd5aa M4.2: the toolchain step
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m36s
CI / e2e (pull_request) Successful in 11m13s
The preflight becomes the toolchain step (D3, as #135 describes it): a
bounded run of the item's driver model, in a transcript of its own,
that makes the build VM and the project's files agree.

- Started by the harness when the VM isn't set up for the project
  (M4.1's checks): it sets the VM up from the repository and edits
  nothing there (no write_file; anything a command changed is reported,
  left for the work).
- Requested by the driver with toolchain_step(reason) once work is
  agreed: it installs, then brings pins, setup docs and CI
  configuration in step; its edits land in the current milestone. The
  driver gets the step's report as the tool's result, not its turns.
- D1 made structural: until the go-ahead the fast path refuses what it
  knows changes something (and anything dangerous), with a pointer to
  the restatement; unknown commands still go to the classifier, so a
  bug can be reproduced before it's restated.
- A `toolchain` actor and view; the walk asks for steps now and then.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 17:37:21 +00:00
dongar 97ea451629 Merge pull request 'Architecture: the toolchain step (D3), and only looking before the go-ahead (D1)' (#135) from toolchain-step-arch into main
CI / checks (push) Successful in 2m33s
CI / e2e (push) Skipped
Reviewed-on: #135
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-10-08 17:28:56 +00:00
dongarandClaude Opus 5.5 66c8ded409 Architecture: the toolchain step (D3), and only looking before the go-ahead (D1)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m54s
CI / e2e (pull_request) Successful in 4m1s
D3's preflight and install recording become the toolchain step: a
bounded run of the item's driver model that makes the build VM and the
project's own files (pins, setup docs, CI configuration) agree, with
the repository as the record. The harness starts it when the VM isn't
set up for the project (and then it edits nothing in the repository);
the driver requests it once work is agreed, and its edits land in the
current milestone. Versions stay facts the harness reads itself; a
rebuilt VM's new host key is trusted only on the operator's word.

D1 gains the rule that until the go-ahead the agent only looks:
anything that changes something is refused by the fast path, so what
the work needs goes in the restatement.

The Development Plan's M4.2 follows: the toolchain step, in place of
recording installs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 17:18:30 +00:00
dev-agent 33f9a75bac Merge pull request 'Expert and integration roles on claude-haiku-5-5' (#134) from haiku-5-5 into main
CI / checks (push) Successful in 2m51s
CI / e2e (push) Skipped
2026-10-08 16:50:14 +00:00
dongarandClaude Opus 5.5 5e1086c501 Expert and integration roles on claude-haiku-5-5
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m6s
CI / e2e (pull_request) Successful in 3m58s
claude-haiku-4-5 is being replaced by claude-haiku-5-5 in LiteLLM: the
integration tests' roles and the example config's expert move to it.
Its profile keeps the predecessor's benchmark figures, marked as such
until it's measured. The plan's models-by-role line also gains the
local classifier (M3.2). Results measured on 4.5 stay as they were.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 16:38:06 +00:00
dongar b6eaceefa2 Merge pull request 'M4.1: toolchain preflight' (#133) from preflight into main
CI / checks (push) Successful in 2m43s
CI / e2e (push) Skipped
Reviewed-on: #133
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-10-08 16:29:41 +00:00
dongarandClaude Opus 5.5 9968a0922a M4.1: toolchain preflight
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m59s
CI / e2e (pull_request) Successful in 10m33s
A model works out and sets up what a project needs on its build VM;
code makes sure that happens, bounds it and checks the result (D3).

- Before each driver run on a project, one round trip checks the build
  VM: the harness's own baseline (missing: the item waits, a template
  problem), the workspace clone (missing: cloned again and said; the
  item resumes from what it pushed) and a per-project marker.
- No marker (a new project, a new or wiped VM): the item's driver model
  runs a bounded preflight first, with its own prompt, the execution
  tools (screened as ever) and toolchain_ready. The harness runs each
  reported version command itself (version-shaped commands only, even
  from the marker, which the model's commands could edit) and writes
  the marker. Gaps and an unfinished preflight are facts and work goes
  on; the thread hears only of installs and gaps. A rebuilt VM starts
  from the last toolchain.
- Later runs check the versions again; a change is a fact and a thread
  line. The PR body says what the work was tested with.
- A changed host key is its own failure (HostKeyChanged): the item
  waits for the operator's `trust new host key`, which drops the pin;
  then the VM is set up again.
- A prompt line: install what the work needs on the build VM, say why,
  and put lasting needs in the project's own pins.
- tuning.preflight: max_turns, command_timeout_s.
- The property walk gains a VM wipe and ready_before_work (checked
  against a mutant that skips preflight); parked_work_safe accepts that
  a wipe loses what wasn't pushed (D3).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 15:43:30 +00:00
dongar 4564cd6f3f Merge pull request 'Say a local commit publishes nothing (#129)' (#132) from commit-note into main
CI / checks (push) Successful in 2m18s
CI / e2e (push) Skipped
Reviewed-on: #132
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-10-08 14:42:24 +00:00
dongarandClaude Opus 5.5 c4726d5673 Say a local commit publishes nothing (#129)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m54s
CI / e2e (pull_request) Successful in 3m15s
The local driver sometimes ran `git commit` itself instead of calling
finish_milestone, then told the operator a PR was waiting when none had
been opened. A commit still runs (it's contained, and publishing takes
whatever is there), but when the driver's command commits anywhere in
its line, the harness adds to its result: committed locally only;
nothing is published until finish_milestone, and there is no PR until
then. Structural, at the moment the model would draw the wrong
conclusion (P6), rather than left to it noticing.

Closes #129

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 14:28:59 +00:00
dev-agent bd17c74c3a Merge pull request 'M3.3: the trust dial (the agent merges approved PRs)' (#131) from trust-dial into main
CI / checks (push) Successful in 2m29s
CI / e2e (push) Skipped
2026-10-08 14:02:42 +00:00
dongarandClaude Opus 5.5 ab469b60bb M3.3: the trust dial (the agent merges approved PRs)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m29s
CI / e2e (pull_request) Successful in 11m23s
A project's trust.merge is operator (the default) or approved (L3, D5,
#65). Set to approved, the harness, never the model, merges a PR in the
PR watch once an approving review from a gitea.operators account is on
its current commit, its checks pass (none at all counts; pending
waits) and it's mergeable; the item then goes on as after any merge.
Each reason to hold off (failed checks, conflicts, a refused merge) is
said once per commit.

Gitea enforces it: protection requires approvals from an operators-only
whitelist and dismisses stale ones. At start, for each approved
project, the agent checks by behaviour that its own unapproved merge is
refused, with a throwaway branch and PR made through the API (never the
workspace); if it isn't refused, the operator merges until a restart.

- gitea: approvals, combined check status, a PR's head commit and
  mergeability, API-made branches and their deletion.
- Test and bring-up repositories get the protection.
- Tests: the decision (unit), the hard wall on a real Gitea
  (test_trust_live), an e2e test where the operator only approves, and
  a walk property, merges_only_approved, checked against a mutant that
  ignores which commit was approved.

Closes #65

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 13:48:16 +00:00
dev-agent 21cbf1682c Merge pull request 'M3.2: the action classifier' (#128) from classifier into main
CI / checks (push) Successful in 2m20s
CI / e2e (push) Skipped
2026-10-08 12:43:39 +00:00
dongarandClaude Opus 5.5 089af0c391 M3.2: the action classifier
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m20s
CI / e2e (pull_request) Successful in 12m31s
What the fast path calls unknown is judged by an independent model call
on the item's classifier role (L4), before it runs.

- nisse_dev.loop.classifier: it sees the action and the agreed scope
  (project, repository, issue number, the restatement the operator said
  go to), built in code; never the transcript, the issue's text, tool
  output or the driver's reasoning. Its verdict is a tool call: safe
  runs; in_scope runs and is reported in the thread; needs_discussion
  asks, as a dangerous action does. An error, no verdict or an unknown
  one fails closed. Verdicts are cached per item, agreed revision and
  action. A helper is refused what needs discussion.
- action_verdict gains source, model and the classifier's exact input;
  details shows the source. An allow after the classifier asked is an
  override. scripts/classifier-evals.py exports the judgements from a
  store, read-only; tests/integration/classifier_cases.yaml is the seed
  set, run per model with --capability-models.
- The fast path's delete rule: deleting on the build VM is contained;
  only what breaks the host (system and home directories) stays
  dangerous, since in tests the build VM is the test machine.
- The classifier role is the local model in the example config.
- The property walk gains a classifier answering at random, junk and
  failures included; dangerous_only_when_allowed now covers its
  verdicts (checked against a mutant that runs them all).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 12:09:38 +00:00
dongar 5044af371b Merge pull request 'M3.1: action classes and the fast path' (#127) from action-classes into main
CI / checks (push) Successful in 2m9s
CI / e2e (push) Skipped
Reviewed-on: #127
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-10-08 11:25:48 +00:00
dongarandClaude Opus 5.5 ea4376eaa1 M3.1: action classes and the fast path
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m10s
CI / e2e (pull_request) Successful in 10m30s
Every execution tool call, the driver's and a helper's, gets a verdict
from code before it runs (P3, P4, D6).

- nisse_core.actions, nisse-core's first code: Action and Verdict, and
  reading shell commands statically, segment by segment (cd, wrappers,
  sh -c). What can't be read that way is unknown; a line is as bad as
  its worst command.
- nisse_dev.loop.actions, the rule table: reading, checks and tests are
  harmless; workspace edits, local git and installs on the build VM are
  contained; pushing, other hosts, uploads, publishing, the forge's own
  tools, deleting outside the workspace and wrecking the VM are
  dangerous; changing branches by hand is refused (the harness owns
  branches) with a pointer to the milestone tools.
- A dangerous call isn't run. The item waits on the operator with a
  question rendered from the action, and `allow` lets exactly that
  action run once. A helper can't ask, so it is refused and told to
  hand back. Unknown actions run, logged, until the classifier (M3.2).
- Every verdict is an action_verdict event, which no model sees, and an
  oplog line; `details` shows the counts and every action not simply
  run.
- The property walk gains dangerous_only_when_allowed (checked against
  a mutant that doesn't use the allow up); the slice e2e checks every
  call was judged and none needed an allow.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 04:32:13 +00:00
dev-agent d0fde2f136 Merge pull request 'CI: a newer run cancels the one it supersedes' (#126) from ci-concurrency into main
CI / checks (push) Successful in 2m16s
CI / e2e (push) Skipped
2026-10-08 03:48:08 +00:00
dongarandClaude Opus 5.5 8840facceb CI: drop the temporary event job
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m50s
CI / e2e (pull_request) Successful in 4m33s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 03:41:17 +00:00
dongarandClaude Opus 5.5 b80225d821 CI: tell e2e:full label events by the PR's labels
CI / checks (push) Skipped
CI / event (pull_request) Successful in 3s
CI / checks (pull_request) Skipped
CI / e2e (pull_request) Skipped
Gitea leaves github.event.label empty.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 03:40:36 +00:00
dongarandClaude Opus 5.5 8bfc1e5f24 CI: Gitea's label events are label_updated in the payload
CI / checks (push) Skipped
CI / e2e (pull_request) Canceled after 27s
CI / event (pull_request) Successful in 2s
CI / checks (pull_request) Successful in 1m50s
Temporarily prints the event, to check what Gitea sends.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 03:38:05 +00:00
dongarandClaude Opus 5.5 9c181d973a Development Plan: superseded runs are cancelled by CI now
CI / checks (push) Skipped
CI / e2e (pull_request) Canceled after 0s
CI / checks (pull_request) Canceled after 1m33s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 03:36:19 +00:00
dongarandClaude Opus 5.5 ef29a1271e CI: a newer run cancels the one it supersedes
CI / checks (push) Skipped
CI / e2e (pull_request) Canceled after 0s
CI / checks (pull_request) Canceled after 23s
Workflow-level concurrency, per PR or branch, with cancel-in-progress.
The earlier attempt (job-level, on the e2e job) didn't cancel anything;
Gitea documents the workflow-level form. A label other than e2e:full
starts a run whose jobs all skip, so it gets a group of its own and
can't cancel the real run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 03:36:02 +00:00
dev-agent 5b62079031 Merge pull request 'M2.8: switch over to the new loop' (#125) from switch-over into main
CI / checks (push) Successful in 2m18s
CI / e2e (push) Skipped
2026-10-08 03:27:00 +00:00
dongarandClaude Opus 5.5 6ae2375194 M2.8: switch over to the new loop
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m16s
CI / e2e (pull_request) Successful in 10m51s
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
2026-10-08 03:12:58 +00:00
dongar 09232dcbdd Merge pull request 'Fixes from the rest of the live session' (#122) from live-findings into main
CI / checks (push) Successful in 2m45s
CI / e2e (push) Skipped
Reviewed-on: #122
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-10-08 02:36:51 +00:00
dongarandClaude Opus 5.5 0064ec7696 Fixes from the rest of the live session
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m13s
CI / e2e (pull_request) Successful in 3m22s
- A near miss for abandon ("abort", "cancel", "drop it", ...) in an item's
  thread is asked about, and changes nothing.
- The driver is told only the operator stops or closes an item, so it
  doesn't claim to have done either.
- An issue assigned again after its item closed starts a new item that
  notes the one before (Gitea's timeline gives the assignment time);
  item_for_issue returns the latest item.
- Abandoning an issue Gitea won't close because of open dependencies says
  so, naming them.
- An agreed item isn't offered restate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 02:16:51 +00:00
dev-agent 613a2a7f72 Merge pull request 'Fixes from the first live session of the new loop' (#119) from live-fixes into main
CI / checks (push) Successful in 2m30s
CI / e2e (push) Skipped
2026-10-08 01:01:04 +00:00
dongarandClaude Opus 5.5 659915d309 Fixes from the first live session of the new loop
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m33s
CI / e2e (pull_request) Successful in 10m38s
Found live on 2026-10-07 (bring-up on nisse-testing-operator/nisse-bringup,
Opus as driver):

- The driver's prose nudge went into the system prompt, so the retry ended
  on the model's own reply. Opus 5.5 refuses that ("does not support
  assistant message prefill. The conversation must end with a user
  message"). The nudge is now a harness fact in the conversation. The
  delegation harness's prose path did the same and is fixed likewise.
- A helper's first request ended with its own delegation_started event,
  rendered as an assistant message: every delegation would have failed on
  Opus. The helper now sees it as the harness handing over its task.
- model_contract checks that every request ends on the user's side, so
  scripted tests catch this from now on (each fix is mutation-checked).
- `status` (and `details`) in an item's thread were taken as replies: they
  woke the driver and cancelled a bug's countdown. They now answer at once
  and change nothing.
- The driver had no way to answer a remark without changing the item, so it
  answered in prose. Every run woken by a message (or a review comment) now
  offers `reply`, which answers where asked and returns to the very wait
  that was interrupted.

Also: scripts/bringup-repo.sh makes the bring-up's test repository
(make_repo/delete_repo, split out of fresh_repo), and made repositories get
the agent's labels (bug, discuss, design).

Tests: test_loop_live_fixes.py (one per finding); the walk sends `status`
into threads (status_changes_nothing) and its scripted driver sometimes
answers remarks with reply.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 00:02:50 +00:00
dev-agent 572bd9f8eb Merge pull request 'M1.5 accepted: tests over a published image' (#118) from image-accept into main
CI / checks (push) Successful in 2m40s
CI / e2e (push) Skipped
2026-10-07 23:14:45 +00:00
dongarandClaude Opus 5.5 d7e4fd0938 M1.5 accepted: the tests run over a published image
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m10s
CI / e2e (pull_request) Successful in 2m54s
- The Dockerfile's test stage stacks on TEST_BASE: this build's agent by
  default, or a published image. It adds only the test tools (uv export
  of the dev group into the image's environment), never reinstalling the
  agent, so the suite tests the image as published.
- scripts/e2e.sh --image passes NISSE_E2E_MODEL into the container.
- The plan marks M1.5 done: v0.1.0's published image passed the whole e2e
  suite (9 of 9, 11m37s), with the release's own tests over it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 23:09:08 +00:00
dev-agent 13efa54f16 Merge pull request 'Test levels: smoke e2e on PRs, full set on e2e:full and before a release' (#117) from test-tiers into main
CI / checks (push) Successful in 2m42s
CI / e2e (push) Skipped
2026-10-07 22:55:15 +00:00
dongarandClaude Opus 5.5 28092194fe E2E on qwen3.8-27b without thinking
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m14s
CI / e2e (pull_request) Successful in 3m6s
The e2e roles move to qwen3.8-27b-nothink, a LiteLLM alias for the same
model with chat_template_kwargs enable_thinking false. With thinking, a
growing share of answers ran to 2-8k tokens (two hit the 8192 cap), which
took one test from 3.5 to 20 minutes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 22:49:13 +00:00
dongarandClaude Opus 5.5 ff5a7a644f CI: install sshd from a fast mirror, or not at all
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m10s
CI / e2e (pull_request) Successful in 9m32s
archive.ubuntu.com has been crawling (a few KB/s), taking the sshd install
in every job from 8 seconds to 3.5-9.5 minutes (another agent measured it;
the runner and inference-4090 were healthy). scripts/ci-sshd.sh skips the
install when the image has sshd, and otherwise points apt at
mirrors.edge.kernel.org first (both source layouts, 22.04 and 24.04).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 22:29:41 +00:00
dongarandClaude Opus 5.5 e35aea5045 Test levels: smoke e2e on PRs, the full set on e2e:full and before a release
- Every PR: lint, unit tests across the runner's cores (pytest-xdist), 16
  property walks from a seed that moves with the run number, integration
  tests, and the end-to-end smoke tests (the slice, a conversation filed
  as work, a question; `smoke` marker).
- A PR labelled e2e:full, a push to main, or a manual run: all 48 walks and
  the whole end-to-end suite. Adding e2e:full to a PR runs it; any other
  label runs nothing.
- A published release runs the full set before its image is built.
- The walk's seeds come from NISSE_WALK_SEEDS / NISSE_WALK_FIRST (48 from 0
  by default).

Measured here: the unit tier on 2 workers took 3m49s against 6-8 minutes
serially.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 22:29:41 +00:00
dev-agent 07a2eb9d10 Merge pull request 'Wait out LLM outages with backoff (L5)' (#116) from llm-retry into main
CI / e2e (push) Skipped
CI / checks (push) Skipped
Release / build-and-push (release) Successful in 19s
v0.1.0
2026-10-07 22:29:40 +00:00
dev-agent cd9eb3fd76 Merge pull request 'The agent as a container image (M1.5)' (#115) from image into main
CI / checks (push) Successful in 5m44s
CI / e2e (push) Skipped
2026-10-07 21:48:43 +00:00
dongarandClaude Opus 5.5 ad8a07fcb7 Wait out LLM outages with backoff (L5: outages delay, never fail)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 6m14s
CI / e2e (pull_request) Successful in 30m8s
A 502, 503 or 504 from the LiteLLM proxy, or a dropped connection, is
retried after 5, 15 and 45 seconds before the call fails. Twice on
2026-10-07 a short burst of 502s stopped work outright: the integration
tier at 16:39 and an e2e conversation at 20:43, where the item waited on
the operator after one refused call.

Not retried: a 500 (it may be the request's fault), a timeout (it already
had its request_timeout_s), or a budget refusal. Rate limits keep their
single 20-second retry. The time spent waiting is reported with the rate
limit's (last_rate_limit_wait_s), so it isn't counted as the model's.

Tests: an outage waited out, one that outlasts the backoff, a dropped
connection, a 500 and a timeout not retried, a budget refusal not waited
on. The offline agent in test_agent_build skips the backoff (its proxy
doesn't exist).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 21:11:45 +00:00
dongarandClaude Opus 5.5 6af54790a9 CI: no image build on PRs; images are built only on release
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 12m10s
CI / e2e (pull_request) Successful in 34m0s
The build-only image job on every PR goes: images are built when a
release is published (release.yml), so PRs don't wait on it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 21:00:21 +00:00
dongarandClaude Opus 5.5 2766cf1e8e Release: the dev organization's registry, pushed by registry-push
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 6m28s
CI / image (pull_request) Successful in 21s
CI / e2e (pull_request) Canceled after 1m27s
The repository moved to the dev organization: images go to
gitea.dongar.net/dev/nisse-dev, pushed by the registry-push bot with the
organization's REGISTRY_TOKEN.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 20:24:51 +00:00
dongarandClaude Opus 5.5 4d42040a34 Release: build on a published release, as in inlaw_outlaw
- on: release (published), not on any v* tag: only a deliberate release
  builds an image; the version is its tag without the v, plus latest.
- docker/login-action, setup-buildx and build-push-action, as the runner
  already serves them for inlaw_outlaw; the version as a build argument
  and image label.
- Comments on why the registry needs a personal token (the job token's
  empty package scope) and that packages belong to the repository owner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 20:14:56 +00:00
dongarandClaude Opus 5.5 0660fa3c51 The agent as a container image (M1.5)
CI / checks (push) Skipped
CI / e2e (pull_request) Failing after 49m48s
CI / checks (pull_request) Successful in 10m0s
CI / image (pull_request) Successful in 27s
- Dockerfile: built with uv into /opt/nisse (installed, not editable),
  run on python:3.12-slim with openssh-client as user nisse; stateless,
  with state in a /var/lib/nisse-dev volume, config mounted at
  /etc/nisse-dev/nisse-dev.yaml, secrets as environment variables, keys
  mounted read-only. 202 MB. Its `test` stage adds the tests and their
  tools over the installed package (UV_NO_SYNC, so `uv run` never swaps
  in the source tree); the default target is the agent.
- scripts/e2e.sh --image REF runs the suite inside an image's test stage,
  on its own sshd.
- CI builds both images on every PR (nothing pushed); release.yml pushes
  gitea.dongar.net/dongar/nisse-dev:<tag> and :latest on a v* tag, with
  the REGISTRY_TOKEN secret.
- scripts/bringup.sh builds the code under test into an image and runs it
  in the systemd unit (root, $E2E mounted at its own path); `plan` prints
  what it would run; the build-VM set-aside reads the projects: config.

Checked: the image refuses to start without a config, runs as uid 10001;
one e2e test passed from the test stage (2m45s, $0).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 19:50:59 +00:00
dev-agent 2d6aa98f87 Merge pull request 'Conversation on the new loop (M2.7)' (#114) from conversation into main
CI / checks (push) Successful in 7m0s
CI / e2e (push) Skipped
2026-10-07 19:20:35 +00:00
dongarandClaude Opus 5.5 8994b3fb4b Conversation on the new loop (M2.7)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 13m26s
CI / e2e (pull_request) Successful in 38m44s
- Questions never change state (L5, #77): a message ending in "?" to a
  waiting project item starts a question run (reply and look-only tools,
  an instruction to change nothing), and the item goes back to the very
  wait it was in: a restatement up stays up and a go binds to it; the
  agent's own question stays open.
- Explanations: a restatement gives its reasons and the main alternative;
  a revision says what changed (restate(changed), sent back without it);
  a milestone's summary says what was done, verified and left open.
- Progress in prose (#42): the driver's and a helper's own words for a
  turn are posted, trimmed, at most once a minute per item; `details`
  shows the last tool calls; a message arriving mid-run is acknowledged at
  once ("Noted; ...").
- CI: the e2e job's comments and the plan no longer claim a newer push
  cancels the run it supersedes (Gitea here doesn't); the concurrency
  block goes.

Tests: conversation unit tests (questions in a go-ahead and an answer wait,
an unanswerable question, revisions, progress rate and trimming, details,
mid-run notes), the helper's progress, the walk asking questions in every
state with a questions_change_nothing property (routing questions the old
way fails 32 of 48 seeds), and an e2e question while a restatement is up.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-07 18:01:51 +00:00
dev-agent 5967d022b6 Merge pull request 'Roles, capability profiles and help on the new loop (M2.6)' (#113) from roles into main
CI / checks (push) Successful in 6m6s
CI / e2e (push) Skipped
2026-10-07 17:40:45 +00:00