Commit Graph
183 Commits
Author SHA1 Message Date
dongarandClaude Opus 5.5 4f152af170 Flatten harness branch names so a model's branch can't block them
CI / checks (push) Skipped
CI / checks (pull_request) Failing after 55s
Live run 3: the executor ran `git checkout -b <task id>` itself, and
publishing then failed, because git can't create '<task>/milestone-1'
while a branch named '<task>' exists. Milestone and delegation branches
are now '<task>-milestone-N' and 'delegation-milestone-N-F'. D4's
harness-owned branches (M2.1) remove the cause; this removes the
collision on the current loop.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 22:29:20 +00:00
dongarandClaude Opus 5.5 c931ac939b Let pushes go by an HTTPS token or an SSH deploy key
CI / checks (push) Skipped
CI / checks (pull_request) Failing after 56s
The push credential is either a push-only account's token, handed to git
through a credential helper that names environment variables, never
values, or a deploy key in a short-lived ssh-agent. The remote URL
follows the credential. Configured as gitea.push_user and push_token, or
gitea.push_key.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 21:59:56 +00:00
dongarandClaude Opus 5.5 761ebdb857 Run every command on the build VM over SSH (M1.3)
CI / checks (push) Skipped
CI / checks (pull_request) Failing after 55s
execution.SSHHost.execute is now the only way the agent runs anything
(L13). Commands travel as a script on SSH's stdin, so the secrets handed
to a call are never on a command line, and every value handed out is
redacted from the output. Timeouts kill the remote process group, and
an unreachable host is reported as such, not as a failed command.

Workspace and GitRepo run on the build VM. The clone uses the
repository's SSH URL, so no credential is stored in it (#89); fetches
and pushes get the push key over stdin, in a short-lived ssh-agent,
and PRs are still opened from the agent's host with its API token.
The bench runs on the build host too.

Tests reach "the build VM" through a throwaway local sshd (nisse_dev
.testing); CI installs openssh-server. The live push test now pushes
with the sandbox's deploy key (NISSE_PUSH_KEY).

Fixes #89.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 21:39:46 +00:00
dev-agent b7f1f3371c Merge pull request 'Model the agent loop as the architecture describes it' (#91) from loop-model into main
CI / checks (push) Successful in 2m7s
2026-10-02 19:36:45 +00:00
dev-agent d407db72c2 Merge pull request 'Revise the deployment model: an agent host and tool VMs' (#90) from deployment-model into main
CI / checks (push) Successful in 1m24s
2026-10-02 19:36:33 +00:00
dongarandClaude Opus 5.5 2187c24d0d Address review comments on the architecture
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m22s
Name the action classes harmless, contained and dangerous instead of
numbering them; say what escalation doesn't depend on; say when a work
item's Matrix thread starts and how threads route commands; add open
questions for the state store's engine, ops issuing credentials, and
releases.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 19:33:17 +00:00
dongarandClaude Opus 5.5 8c38444b88 Close five gaps the loop model found
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m18s
- abandon closes the item's open PRs
- a PR the operator closed is an answer, never reopened by a retry
- a review fix for a PR that merged meanwhile goes out as a new PR
- redoing a milestone after a closed PR gets a new branch and PR
- a record issue carries its work item's ID from creation

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 19:24:54 +00:00
dongarandClaude Opus 5.5 76e9fa38c7 Model the agent loop as the architecture describes it
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m24s
A Quint model of the design in docs/Architecture.md (PR #90): intake,
restatements and go-aheads, park-and-switch, blocked dependencies,
harness-owned milestones, waiting for merges, review follow-ups, record
issues, crashes and build VM wipes. It found five gaps; the model carries
the proposed fixes, and loop_scenarios.qnt keeps each counterexample.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 19:24:39 +00:00
dongarandClaude Opus 5.5 5839091d65 Define projects as configuration entries (D8); note the agent host's recovery
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m19s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 18:53:45 +00:00
dongarandClaude Opus 5.5 4ca8e1f550 Drop Vikunja: Gitea issues are the one tracker for every agent
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m17s
Ops work lives in one infrastructure repository for both sites, with
site labels; intake, labels and dependencies work as for nisse-dev, and
agent-to-agent requests are cross-repo issues.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 18:52:18 +00:00
dongarandClaude Opus 5.5 6e4dca040c Condense the architecture; move mechanism detail to the Development Plan
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m18s
Each rule is stated once in its home section and referred to by ID
elsewhere. No decision added or removed; implementation detail moved to
Development-Plan.md section 6, keyed by architecture section. P8 folded
into P1, P5 and X1; the Appendix about the old spec dropped. Section
IDs are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 18:43:16 +00:00
dongarandClaude Opus 5.5 d77c142488 Accept a plain-language go-ahead, announced and revision-bound
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m16s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 18:35:00 +00:00
dongarandClaude Opus 5.5 349ec794f2 Fix inconsistencies and gaps found re-reading the architecture
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m27s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 18:30:32 +00:00
dongarandClaude Opus 5.5 acf7ce191c Let the agent file issues from conversation (later addition)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m17s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 18:26:58 +00:00
dongarandClaude Opus 5.5 cd2ff6d769 Write the agreed dev workflow into the architecture
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m19s
Issues assigned to dev-agent are the intake; the agent restates its
understanding and either proceeds (bug label) or waits for a go-ahead.
Labels steer and are never required. Build plans and ARCHITECTURE.md
are working documents reviewed in PRs, not gates; scope is the issue
and its conversation. Work items can be blocked on Gitea dependencies,
each gets a Matrix thread, milestones are started and finished by
control tools, and the next milestone waits for the previous merge.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 18:26:02 +00:00
dongarandClaude Opus 5.5 3e7edce6dc Make the vault's git history optional
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m20s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 17:37:45 +00:00
dongarandClaude Opus 5.5 aa97309a0c Replace Outline with an Obsidian-format knowledge vault on the agent host
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m21s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 17:30:54 +00:00
dongarandClaude Opus 5.5 230b927ebe Revise the deployment model: an agent host and tool VMs
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m20s
Agents run as containers on one stable agent host that keeps their
state and secrets and never runs a command a model chose. Model-chosen
commands run on disposable tool VMs over SSH: a build VM per project
for nisse-dev, its own tool VM for nisse-ops. Secrets are a set per
agent, handed out per operation by the harness and redacted from tool
output. MCP servers stay on the agent host as the one exception.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 16:35:55 +00:00
dongar 7b031179a2 Merge pull request 'Give each role its own view of the transcript (M1.2)' (#88) from transcript-views into main
CI / checks (push) Successful in 1m26s
Reviewed-on: dongar/nisse#88
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-10-02 03:18:09 +00:00
dongarandClaude Opus 5.5 3fba958958 Give each role its own view of the transcript (M1.2)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m27s
A model no longer reads another role's turns as its own (#78). The log
stays one record per work item; what a role is shown is projected from
it by Transcript.view() (views.py), which the planner, executor and
helper now build their context from:

- own events unchanged;
- operator messages labelled with their channel, e.g.
  [operator, Matrix] or [operator, comment on PR #165];
- orchestrator facts (a new `fact` event) as [orchestrator] statements;
- the executor sees a helper's handback as [helper report], not its turns;
- a helper sees the approved plan and the executor's milestone work as
  one-line narration;
- the planner sees no other role's work at all.

The orchestrator now writes facts at the points the models must know
about: work item opened, plan approved or rejected, milestone started,
finished or stopped, published (with PR number and URL) or not, review
comment answered, next milestone, task completed or abandoned.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-02 00:39:38 +00:00
dongar 51e144e26b Merge pull request 'Work items in the state store, with a transcript bound to each (M1.1)' (#87) from work-items into main
CI / checks (push) Successful in 1m15s
Reviewed-on: dongar/nisse#87
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-10-01 23:36:36 +00:00
dongarandClaude Opus 5.5 83804fe02a Put work items in the state store, and bind the log to them (M1.1)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m14s
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
2026-10-01 23:20:29 +00:00
dongarandClaude Opus 5.5 3cf4cefacd Rename the delegate and consultant roles to helper and expert
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
2026-10-01 23:04:37 +00:00
dev-agent e6aaa4783d Merge pull request 'Add a bring-up script and a transcript dump; let nisse-dev install on its own VM (M0.2)' (#86) from bringup-script into main
CI / checks (push) Successful in 1m28s
2026-10-01 16:18:15 +00:00
dongarandClaude Opus 5.5 a18321b4b0 Add a bring-up script and a transcript dump; let nisse-dev install on its own VM (M0.2)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m16s
Development Plan M0.2.

- scripts/bringup.sh: start [REF] sets the previous run aside under
  /root/nisse-e2e/runs/, exports the ref under test and starts the agent
  as the transient systemd unit nisse-bringup, so it outlives the shell
  that started it; stop (SIGTERM, clean shutdown), status, logs, and
  collect (the journal log and a readable transcript).
- nisse_dev.dump (python -m nisse_dev.dump STATE_DB [--full]): a readable
  dump of a state store's state, pending intents and canonical log. Opens
  the store read-only, so it is safe against a live agent.
- The agent runs as root on this development VM, as it will on its own
  VM. Architecture D3/D6/D7 corrected to match the operator's intent:
  installing packages and toolchains on its own VM is class 2, recorded
  so the environment can be rebuilt (it was class 3 with provisioning an
  open question); egress names OS package mirrors. The plan's M0.2, M4.1,
  M4.2 and bring-up guards follow.

Acceptance: a smoke run started, answered status in the test room,
stopped cleanly, and collect produced the log and transcript.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-01 16:16:32 +00:00
dongar 678466c176 Merge pull request 'Retire the old build spec, and point every reference at the architecture (M0.1)' (#85) from retire-old-spec into main
CI / checks (push) Successful in 1m15s
Reviewed-on: dongar/nisse#85
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-10-01 00:52:53 +00:00
dongarandClaude Opus 5.5 1ad0b2147d Retire the old build spec, and point every reference at the architecture (M0.1)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m22s
Development Plan M0.1. Every section reference in code, tests, specs/,
the example config, the tuning guide, and the CLAUDE.md and README files
now cites docs/Architecture.md (P, L, D, O, X, Q) instead of the old
spec's A-H, and docs/Architecture-Build-Spec.md is deleted (git keeps
it). The plan's old-to-new table goes with it.

Mostly mechanical, with a per-site choice where one old section split:
A4 became L2 where it meant stop/abandon/the loop and L3 where it meant
approvals; A10 became L6 in the context-budget code and L7 elsewhere; B2
became D4 for outbound Gitea and publishing and D5 for review intake;
'A3 step N' became the matching step of L8's boot sequence. Comments
describing behaviour the architecture drops (planning always escalating,
thresholds tuned at an old build step) now describe the current code and
point at the Development Plan.

docs/Architecture.md gains one line the rewrite missed: intake outages
delay but never fail work (old B2), now in L5, where the poller and
Matrix code cite it.

Comments and docs only; no behaviour change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-01 00:17:48 +00:00
dongar 890708594b Merge pull request 'Propose the Development Plan' (#84) from development-plan into main
CI / checks (push) Successful in 1m14s
Reviewed-on: dongar/nisse#84
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-30 21:07:39 +00:00
dongarandClaude Opus 5.5 ae6513fd26 Development Plan: say which open issues are superseded, and which stay open
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m15s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-30 21:06:13 +00:00
dongarandClaude Opus 5.5 f53b2777bc Propose the Development Plan, and mark the architecture agreed
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m22s
docs/Development-Plan.md: where the code stands against each section of
docs/Architecture.md, the environment today, how we work (plan-first per
milestone, tests, the Quint model, a bring-up run on this box ending
each phase), and seven phases of milestones from housekeeping to
nisse-ops, each marked pre-approved or needs discussion. Every open
issue is mapped to a milestone or marked superseded; an old-to-new
section table serves the reference update in M0.1.

docs/Architecture.md: status now agreed; D7 regains the browser tool
for the internal test network, left out of the rewrite by mistake.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-30 19:47:19 +00:00
dongar 73a035dfcc Merge pull request 'Draft: rewrite the architecture (docs/Architecture.md)' (#83) from architecture-rewrite into main
CI / checks (push) Successful in 1m14s
Reviewed-on: dongar/nisse#83
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-30 19:40:21 +00:00
dongarandClaude Opus 5.5 a18294fa0c Draft: rewrite the architecture as docs/Architecture.md
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m18s
A full rewrite of the architecture, following the outline agreed with the
operator: a threat model of mistakes, injection and crashes rather than a
malicious model; actions classed by their effects; hard walls, a
deterministic fast path and an action classifier; a conversational loop
driven by a local model with frontier helpers; approvals as scoped
permissions with pre-approved milestones and a trust dial; context and
memory, including agent memory; documents as nisse-dev's workflow.

Added beside docs/Architecture-Build-Spec.md, which stays until this is
agreed; the Development Plan and reference updates come after. New
section letters (P, L, D, O, X, Q) avoid clashing with the old A-H
references in the code meanwhile.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-30 15:50:24 +00:00
dongar d302d337d6 Merge pull request 'Redeliver an interrupted review comment, and close the issue on completion' (#76) from redeliver-review-and-close-issue into main
CI / checks (push) Successful in 1m25s
Reviewed-on: dongar/nisse#76
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-29 20:37:04 +00:00
dongarandClaude Opus 5.5 848bd9f122 Redeliver an interrupted review comment, and close the issue on completion
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m16s
Fixes counterexamples 6 and 7 from specs/orchestrator.qnt, the last two.

6. A crash while the executor answered a review comment lost it: its
   cursor is saved before the work (at most once), and a restart in
   PR_OPEN resumed quietly. Now pr.reviewing marks the comment until
   the executor returns (every way it returns, but not a crash), and
   boot resumes the watch just before it, with a notice. The mark stays
   until it is answered, so a second crash cannot lose it either.
7. 'project complete' left the plan issue open. It now closes it with a
   note, after IDLE is saved, as abandon does (shared helper).

The model marks and redelivers, and closes the issue; scenarios 6 and 7
now expect their properties to hold. A mutant without redelivery fails
scenario 6. All eight properties held together over 300,000 traces.
No xfail reproductions remain.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-29 20:30:14 +00:00
dongar 83a4bd72b5 Merge pull request 'Adopt an already-published PR instead of publishing again' (#75) from adopt-published-pr into main
CI / checks (push) Successful in 1m15s
Reviewed-on: dongar/nisse#75
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-29 20:14:47 +00:00
dongarandClaude Opus 5.5 8bd1ac0fa2 Adopt an already-published PR instead of publishing again
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m12s
Fixes counterexample 5 from specs/orchestrator.qnt. A crash after the
PR was opened but before PR_OPEN was recorded left the agent in
EXECUTING; the restart question's 'continue' and 'start' then published
again, Gitea refused a second PR, and there was no way back to PR_OPEN.
On the live run the executor model happened to notice the existing
work; recovery must not depend on that.

- Boot (A3 step 4): in EXECUTING, if Gitea shows the milestone's PR
  open, adopt it, watch it from the start, and resume in PR_OPEN with a
  notice. Gitea unreachable or the PR closed: the restart question, as
  before.
- MilestonePublisher.publish is idempotent: after pushing, an open PR
  for the branch is adopted (and so updated) rather than a second one
  opened. This also covers a PR opened whose response was lost.
- The model adopts too, gains the lost-response outcome, and now counts
  PRs opened per milestone (one_pr_per_milestone was a flag the fixed
  model could never set). A mutant without adoption fails the new
  scenario; the properties held over 200,000 traces each.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-29 19:58:19 +00:00
dongar 6fdec6922b Merge pull request 'Bind approvals and feedback to the revision they were about' (#74) from bind-approvals-to-revisions into main
CI / checks (push) Successful in 1m21s
Reviewed-on: dongar/nisse#74
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-29 19:45:07 +00:00
dongarandClaude Opus 5.5 97863c1939 Bind approvals and feedback to the revision they were about
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m16s
Fixes counterexamples 1-4 from specs/orchestrator.qnt: a repeated
'approve', 'approve rev-1' meant for the architecture, approving on both
channels, and 'approve' plus an afterthought all landed on the next
plan, one the operator had not seen.

- Revisions are numbered across the work item (A4 already required
  monotonic tags; the code numbered per gate), so rev-N names one plan.
- Each reply is stamped on arrival with the revision that was up: in
  Matrix the one whose approval request had been sent (a plain value
  the transport thread reads, restored on boot), in Gitea the latest
  plan comment older than the reply (board.posts).
- Gate.evaluate applies a reply that names no revision only if its stamp
  is the current revision. Otherwise an approval is not applied and
  feedback rejects nothing (it is still in the transcript), and the
  agent says which revision is up. 'approve rev-N' binds by its number.
- Spec A4 states both rules. The model applies them; scenarios 1-4 now
  expect the properties to hold, and both held over 200,000 random
  traces and Apalache's exhaustive check to depth 8.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-29 19:13:20 +00:00
dongar 6c83839f4d Merge pull request 'Model the orchestrator in Quint, and reproduce its counterexamples' (#73) from quint-orchestrator-spec into main
CI / checks (push) Successful in 1m22s
Reviewed-on: dongar/nisse#73
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-29 18:56:29 +00:00
dongarandClaude Opus 5.5 9f38e7a467 Ignore Apalache's output directory
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m18s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-29 18:52:06 +00:00
dongarandClaude Opus 5.5 d79e399933 Model the orchestrator in Quint, and reproduce its counterexamples
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m14s
specs/orchestrator.qnt models control.py: states, gate, pending
questions, comment cursors, publish intents and the worker queue, with
operator messages, Gitea comments, work outcomes (exceptions included)
and a crash at any point interleaving freely. Eight properties.

Seven counterexamples, each written out as a trace in
specs/orchestrator_scenarios.qnt and reproduced against the real code in
nisse-dev/tests/test_quint_counterexamples.py (xfail, strict, until
fixed):

1-4 an approval or feedback lands on a plan the operator had not seen:
    a double 'approve', 'approve rev-1' meant for the architecture,
    approving on both channels, and 'approve' plus an afterthought;
5   a crash after the PR opens but before PR_OPEN publishes again and
    cannot get back to PR_OPEN;
6   a crash while answering a review comment loses it silently;
7   'project complete' leaves the plan issue open.

no_stranding, await_input_has_a_question and execution_needs_approval
held over 200,000 random traces of 40 steps, and under Apalache's
exhaustive check to depth 8, which does find counterexample 1 at that
depth. No behaviour changes here.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-29 18:51:59 +00:00
dongar 26dabc3d54 Merge pull request 'Match approvals and commands regardless of case and trailing punctuation' (#72) from case-insensitive-commands into main
CI / checks (push) Successful in 2m11s
Reviewed-on: dongar/nisse#72
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-29 18:00:27 +00:00
dongarandClaude Opus 5.5 6afd0cc914 Match approvals and commands regardless of case and trailing punctuation
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m15s
A phone keyboard's "Approve" did not match the approval pattern, so it
counted as rejection feedback and threw the plan away; "Stop" did not
reach the cancel flag; other capitalised commands got a hint instead of
acting.

gates.command_form() is how a message reads as a command: case,
surrounding whitespace, inner runs of whitespace and a trailing '.' or
'!' do not matter. The approval gate and every command match in the
orchestrator (stop, status, abandon, use, start milestone, next
milestone, project complete, the restart choices) use it. Only matching
changes: the project name in 'use <name>' keeps its case, and feedback
and answers are recorded and passed on as written. A stale revision is
still refused, and approval still needs the whole message to be the
command.

Closes #64.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-29 17:57:34 +00:00
dongar b893fba342 Merge pull request 'Add the abandon command' (#71) from abandon-command into main
CI / checks (push) Successful in 1m15s
Reviewed-on: dongar/nisse#71
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-29 17:52:30 +00:00
dongarandClaude Opus 5.5 d68e3b3a83 Add the abandon command
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m18s
A4 already names abandon as one of the ways out of a halt (continue,
re-plan, or abandon) and 0.2 closes a work item as completed or
abandoned, but no command did it: from a gate or a question the only way
to drop a task was to edit the state database.

- 'abandon' ends the task from any waiting state and returns to IDLE.
  The move is made durable first; then the plan issue and any open
  milestone PR are closed with a note (branches kept), and any
  unresolved intent is aborted so boot reconciliation does not gate on
  work dropped on purpose. A Gitea failure is reported, not fatal.
- Like 'status' it is a command in every state, never read as an answer
  or feedback. While work runs, 'stop' comes first, as before.
- The restart / stranded question and the PR_OPEN hints mention it.
- Spec: B1 gains the abandon edge and B2 the command.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-29 17:43:43 +00:00
dongar 4e8ebea26a Merge pull request 'Keep Gitea threads watched across restarts and interruptions' (#68) from pr-open-handling into main
CI / checks (push) Successful in 1m20s
Reviewed-on: dongar/nisse#68
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-29 17:37:25 +00:00
dongarandClaude Opus 5.5 3b6aae92c5 Keep Gitea threads watched across restarts and interruptions
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m20s
- Record a comment cursor per thread (board.last_comment, the field that
  already existed unused; pr.last_comment), advanced durably when a
  comment is taken up. CommentPoller.resume_after() skips comments at or
  below it, so re-attaching needs no request and cannot replay.
- Boot re-attaches the plan watch at an open gate and the review watch in
  PR_OPEN from their cursors, catching up on comments posted while the
  agent was down (#59). Data saved without a cursor skips what is there,
  so an old approval cannot approve the current revision.
- A restart in PR_OPEN resumes with a notice instead of the restart
  question (#58). A stored restart question for PR_OPEN returns to it on
  either reply instead of re-planning the milestone.
- A stopped or failed review response gets its own pending kind; the
  reply returns to PR_OPEN and resumes the watch from the cursor, so
  comments that arrived meanwhile are delivered. It used to return to
  milestone sign-off, where starting the milestone would open a second PR.
- Comments ignored because of the state leave the cursor alone, so they
  are delivered once the agent is back where they apply.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-29 17:33:41 +00:00
dongar ab932f2ef4 Merge pull request 'Don't assert the exact word in the LiteLLM smoke test' (#67) from smoke-test-no-exact-word into main
CI / checks (push) Successful in 1m17s
Reviewed-on: dongar/nisse#67
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-29 17:24:43 +00:00
dongarandClaude Opus 5.5 1ab5d86a88 Don't assert the exact word in the LiteLLM smoke test
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m24s
The test checks that a request reaches the executor model through
LiteLLM and text comes back. It also asserted the model obeyed "Reply
with exactly one word: pong", which Haiku once answered with "ping" and
failed CI on #66. Assert non-empty text instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-09-29 17:22:24 +00:00
dongar 40b59e77bf Merge pull request 'Never leave the agent stranded in a working state' (#66) from no-stranded-states into main
CI / checks (push) Successful in 1m16s
Reviewed-on: dongar/nisse#66
Reviewed-by: Don Garrett <dgarrett@acm.org>
2026-09-29 17:21:42 +00:00