Commit Graph
5 Commits
Author SHA1 Message Date
dongarandClaude Opus 5.5 154a0c3fa8 M7.1: extract nisse-core
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m42s
CI / e2e (pull_request) Successful in 4m0s
What isn't the dev agent's moves to nisse-core, unchanged in behaviour,
with its tests: canonical, render, render_anthropic, llm, catalog, probe,
context, views, state, reconcile, worker, cancel, dump, transport, matrix,
execution, testing, vault, memory, oplog, envfile and gitea (whole).

- nisse_core.state.StateStore holds the tables every agent has; an
  agent's subclass adds its tables (version, migrations), roles and
  views. nisse_dev.state.StateStore adds the build VM blocks (v5).
- views.project takes what each role is shown, rather than a fixed table.
- Vault takes the agent's name (agent=), for provenance.
- A test that nisse_core never imports an agent.
- The loop, config, git, delegation, expert, tools and evals stay in
  nisse-dev until nisse-ops has a loop to share.

Docs: the Development Plan marks M7.1 done and notes that Phase 6 waits on
the agent host and Terraform; Architecture Part L names the package.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
2026-10-13 18:37:01 +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
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 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
dongarandClaude Opus 5 2b7651fb37 Add ask_expert and eval capture (A14/A16) — step 7c
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m7s
Completes step 7.

- expert.py: one stateless call for a question needing no repo access.
  Structured output is contracted at the prompt level, since vendors
  differ on JSON enforcement, so the reply is parsed and validated
  locally with one retry — tolerating a fenced code block, because
  models wrap JSON habitually, but not missing fields
- The per-task budget is counted from the canonical log rather than
  memory, so it survives a restart and cannot hand a circling executor
  a fresh allowance. Exhaustion raises rather than answering: a caller
  out of consults should get on with the work or escalate
- Consults are recorded as delegation_started/completed with role
  consultant. A16 asks for a "consult/delegation eval set", and
  reusing the composite events gets consult evaluation for free rather
  than inventing a parallel record
- evals.py pairs starts with completions into eval records, with
  verdicts kept outside the canonical log: the log records what
  happened, a verdict is a later judgement about it, and judgements
  get revised. Schema v3 adds the outcomes table, so a v1 store now
  traverses two migrations — a better exercise of the hook than one
- ask_expert is offered to the executor as a tool only when a desk is
  configured, in addition to the observable escalation triggers and
  never instead of them (A12). Consulting is not mutating, so a
  completion claim backed only by consults is still challenged

Found live: passing an empty message list and putting the question in
the system prompt fails outright — the Messages API requires at least
one non-system message. The question now travels as a synthesised user
message that is never appended to the log, which keeps the consult
stateless while remaining a valid request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 19:48:13 +00:00