AGENT SKILL

verify-agent-lifecycle

Formally verify the subagent lifecycle before or after changing it. Use when editing internal/subagent/orchestrator.go's Spawn, trackAgent, forwardAgentEvents, terminalStatus, Cancel, ShutdownWithTimeout or evictCompletedAgentsLocked, or internal/subagent/pool.go — or when a canceled agent is reported as failed, an agent vanishes from the list with no verdict, the orchestrator panics on a pool release, or an agent can be canceled twice.

License 许可
MIT
所属仓库
dimetron/pi-go
最近更新
2026-11-12
Stars
★ 224
Forks
34

01 前言 Front Matter

name
verify-agent-lifecycle
description
Formally verify the subagent lifecycle before or after changing it. Use when editing internal/subagent/orchestrator.go's Spawn, trackAgent, forwardAgentEvents, terminalStatus, Cancel, ShutdownWithTimeout or evictCompletedAgentsLocked, or internal/subagent/pool.go — or when a canceled agent is reported as failed, an agent vanishes from the list with no verdict, the orchestrator panics on a pool release, or an agent can be canceled twice.

03 SKILL.md 正文

Verify the subagent lifecycle

One subagent has six states, three writers, and a pool slot that must be released exactly once. pool.Release is an unguarded channel receive (internal/subagent/pool.go:36-38), so a second release panics the process, and nothing in a single-agent test can produce the interleaving that does it.

The properties: a cancel is never overwritten by the process's own exit reason, an agent that leaves the list has already published its verdict, and exactly one pool slot is released per spawn.

Does my change touch the verified code

git diff --stat <sync-commit>..HEAD -- \
  internal/subagent/orchestrator.go internal/subagent/pool.go internal/subagent/types.go

Model covers (each model step names the function it stands for):

Model action Code
SlotAcquired Spawn past pool.Acquire, orchestrator.go:487-499
SpawnRefused trackAgent refuses after shutdown → abandonSpawn, orchestrator.go:499-505, 599-607
Event / ProcExits the event loop and proc.Wait, orchestrator.go:610-629
Cancel Cancel + validateAgentForCancel, orchestrator.go:806-834
ShutdownRunning / ShutdownIdle ShutdownWithTimeout, orchestrator.go:859-894
PublishKept / PublishEvicted forwardAgentEvents' tail: evict, publish, release, orchestrator.go:628-648, 683-711

Not modelled, and why: the worktree manager (its own stash lifecycle, modelled separately if it becomes a target); spawner.go's event channel and its silent drops when the 64-slot buffer fills; terminalStatus's signal classification (the model takes the outcome as a parameter); recentTasks and the prune loop; concurrency above one agent; Get/List as anything other than a read. A bug in any of those is invisible here.

Run it

hack/formal/check.sh agent           # all rules hold, or the first one that breaks
hack/formal/check.sh agent --chart

Cost: 35 distinct states, under a second. Exit 0 all rules hold, 1 a rule broke, 2 TLC or the environment failed.

Rules, and the harm each one names

Rule The promise What breaking it means for a user
CancelIsSticky a cancel request wins over the process exit reason you press cancel, and pi reports the agent failed instead
PublishedIsTerminal run_done never says running a consumer waits forever for a verdict already sent
EvictionStillPublishes an agent missing from the list has published an agent disappears with no status: neither running nor done
SlotReleasedOnce at most one pool.Release per spawn, and only if acquired pool.Release on an empty semaphore panics the process
NoLateCancel a finished agent is never relabeled canceled a completed result is reported as canceled

Two traps, both found while building this model

A rule phrased on the state the bug overwrites cannot see the bug. The first CancelIsSticky read status = "canceled" => ..., which is trivially true once a later step overwrites status with "failed". The mutation that drops the if status == "running" guard at orchestrator.go:626 passed cleanly, on 361 explored states. The fix is the cancelAsked variable: track the request, and state the rule about what a consumer reads after it. Re-run the mutation after any edit to this rule.

A mutant that does not compile is not a caught regression. Two attempted mutations "failed" because they produced a null variable — a TLA+ error, not an invariant violation. Always read which line fired: it must be Error: Invariant <name> is violated. A TLC threw an unexpected exception or Attempted to apply the operator is a broken mutant, and proves nothing.

Also: go run ./hack/formal/lint/main runs first and catches unassigned variables and conjoined assignments, the two defects that produced false passes in the /goal and /run models.

After confirming the model is still true

  • Sync commit: cc79aceb43df31012e9e5c1f69a7ba1231c32470
  • Constants: none. MaxCompletedAgents was modelled as the eviction decision it produces rather than a counter, because a counter made the state space explode past 2 million.
  • Chart budget: 35 states, so the full chart always draws.

Move the sync commit forward only after confirming the model describes the code at the new commit.

Draw the chart

hack/formal/check.sh agent --chart writes hack/formal/agent/out/report.md and chart.mmd via the pstack /statechart skill.

04 同仓库更多 Skills

dimetron
dimetron/pi-go

verify-session-flow

Formally verify the interactive session flow before or after changing it. Use when editing internal/tui/tui.go's handleKey, handleInterruptKey, handleInputSubmit, ctrlCCount or quitting, internal/tui/agent_loop.go's enqueuePrompt, submit, steerPrompt, cancelAgent or handleAgentDone, or the deferred-init splash — or when a user reports that Ctrl+C quit on the first press, that Esc did not stop a turn or a goal loop, that a queued prompt was lost when they canceled, or that the TUI hung on a spinner nothing could clear.

Skill与智能体开发
★ 224
dimetron
dimetron/pi-go

verify-run-pipeline

Formally verify the /run phase machine before or after changing it. Use when editing internal/tui/run.go's phase transitions (handleRunAgentDone, runGates result handling, verifyRunComplete, retryRun, reportGateHang, merge), internal/sop/verdict.go's Passed, or the checklist refresh — or when a run merged work it should not have, retried a hung gate, lost a cycle at the retry budget, or merged on a stale PASS.

★ 224
dimetron
dimetron/pi-go

verify-history-rewrite

Formally verify the session-history rewrite path before or after changing it. Use when editing internal/session/autocompact.go, internal/session/compaction*.go, internal/session/store.go's Compact/ClearEvents/rewriteEvents, internal/agent/agent.go's Run/RunStreaming/runPreTurn, or internal/tui/commands.go's clearConversation/clearSessionContext and the context gauge — or when a turn is canceled mid-flight, a tool call and its result get separated, a session's history disappears while the agent is still working, or the model is sent a tool result whose call was compacted away.

Skill与智能体开发
★ 224
dimetron
dimetron/pi-go

verify-goal-loop

Formally verify the /goal automatic loop before or after changing it. Use when editing internal/tui/goal.go, internal/tui/agent_loop.go's handleAgentDone/cancelAgent/steerPrompt, or internal/tools/goal.go's GoalController — or when a goal loop will not stop, stops one cycle early, closes a goal that is not met, or restarts a goal you finished.

Skill与智能体开发
★ 224
dimetron
dimetron/pi-go

test-new-skill

暂无简介

测试与质量Skill与智能体开发
★ 224
dimetron
dimetron/pi-go

ponytail

暂无简介

★ 224