[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M70SHPYN1AS6D6RJ8HN270RZ":3,"skill-same:01M70SHPYN1AS6D6RJ8HN270RZ":72},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":49,"commitSha":50,"skillDirectory":51,"forksCount":52,"repositoryHtmlUrl":53,"repositoryHomepageUrl":54,"ownerHtmlUrl":55,"ownerBlogUrl":6,"ownerDescription":56,"updatedAt":57,"install":58,"resources":71},"01M70SHPYN1AS6D6RJ8HN270RZ","verify-agent-lifecycle",null,"Formally verify the subagent lifecycle before or after changing it. Use when editing internal\u002Fsubagent\u002Forchestrator.go's Spawn, trackAgent, forwardAgentEvents, terminalStatus, Cancel, ShutdownWithTimeout or evictCompletedAgentsLocked, or internal\u002Fsubagent\u002Fpool.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.","dimetron\u002Fpi-go","dimetron","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F877433?v=4","01M6E34QX9HKYKMWJVZQEPR2Q3","Dmytro Rashko","01M6E34R2A6563XM0PTZE8KPGG",224,"MIT","https:\u002F\u002Fgithub.com\u002Fdimetron\u002Fpi-go\u002Fblob\u002Fmain\u002Finternal\u002Fextension\u002Fbundled_skills\u002Fverify-agent-lifecycle\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"verify agent lifecycle","agent lifecycle","verify","lifecycle","pool","it","go","list","release","spawn","orchestrator","verdict","with","can","subagent","cancel","when","use","editing","as","2026-11-10 10:12:30",false,"---\nname: verify-agent-lifecycle\ndescription: Formally verify the subagent lifecycle before or after changing it. Use when editing internal\u002Fsubagent\u002Forchestrator.go's Spawn, trackAgent, forwardAgentEvents, terminalStatus, Cancel, ShutdownWithTimeout or evictCompletedAgentsLocked, or internal\u002Fsubagent\u002Fpool.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.\n---\n\n# Verify the subagent lifecycle\n\nOne subagent has six states, three writers, and a pool slot that must be released\nexactly once. `pool.Release` is an unguarded channel receive\n(`internal\u002Fsubagent\u002Fpool.go:36-38`), so a second release panics the process, and\nnothing in a single-agent test can produce the interleaving that does it.\n\nThe properties: **a cancel is never overwritten by the process's own exit\nreason**, **an agent that leaves the list has already published its verdict**, and\n**exactly one pool slot is released per spawn**.\n\n## Does my change touch the verified code\n\n```bash\ngit diff --stat \u003Csync-commit>..HEAD -- \\\n  internal\u002Fsubagent\u002Forchestrator.go internal\u002Fsubagent\u002Fpool.go internal\u002Fsubagent\u002Ftypes.go\n```\n\n**Model covers** (each model step names the function it stands for):\n\n| Model action | Code |\n|---|---|\n| `SlotAcquired` | `Spawn` past `pool.Acquire`, `orchestrator.go:487-499` |\n| `SpawnRefused` | `trackAgent` refuses after shutdown → `abandonSpawn`, `orchestrator.go:499-505, 599-607` |\n| `Event` \u002F `ProcExits` | the event loop and `proc.Wait`, `orchestrator.go:610-629` |\n| `Cancel` | `Cancel` + `validateAgentForCancel`, `orchestrator.go:806-834` |\n| `ShutdownRunning` \u002F `ShutdownIdle` | `ShutdownWithTimeout`, `orchestrator.go:859-894` |\n| `PublishKept` \u002F `PublishEvicted` | `forwardAgentEvents`' tail: evict, publish, release, `orchestrator.go:628-648, 683-711` |\n\n**Not modelled, and why:** the worktree manager (its own stash lifecycle, modelled\nseparately if it becomes a target); `spawner.go`'s event channel and its silent\ndrops when the 64-slot buffer fills; `terminalStatus`'s signal classification\n(the model takes the outcome as a parameter); `recentTasks` and the prune loop;\nconcurrency above one agent; `Get`\u002F`List` as anything other than a read. A bug in\nany of those is invisible here.\n\n## Run it\n\n```bash\nhack\u002Fformal\u002Fcheck.sh agent           # all rules hold, or the first one that breaks\nhack\u002Fformal\u002Fcheck.sh agent --chart\n```\n\nCost: 35 distinct states, under a second. Exit `0` all rules hold, `1` a rule\nbroke, `2` TLC or the environment failed.\n\n## Rules, and the harm each one names\n\n| Rule | The promise | What breaking it means for a user |\n|---|---|---|\n| `CancelIsSticky` | a cancel request wins over the process exit reason | you press cancel, and pi reports the agent failed instead |\n| `PublishedIsTerminal` | `run_done` never says `running` | a consumer waits forever for a verdict already sent |\n| `EvictionStillPublishes` | an agent missing from the list has published | an agent disappears with no status: neither running nor done |\n| `SlotReleasedOnce` | at most one `pool.Release` per spawn, and only if acquired | `pool.Release` on an empty semaphore panics the process |\n| `NoLateCancel` | a finished agent is never relabeled `canceled` | a completed result is reported as canceled |\n\n## Two traps, both found while building this model\n\n**A rule phrased on the state the bug overwrites cannot see the bug.** The first\n`CancelIsSticky` read `status = \"canceled\" => ...`, which is trivially true once a\nlater step overwrites `status` with `\"failed\"`. The mutation that drops the\n`if status == \"running\"` guard at `orchestrator.go:626` passed cleanly, on 361\nexplored states. The fix is the `cancelAsked` variable: track the *request*, and\nstate the rule about what a consumer reads after it. Re-run the mutation after any\nedit to this rule.\n\n**A mutant that does not compile is not a caught regression.** Two attempted\nmutations \"failed\" because they produced a `null` variable — a TLA+ error, not an\ninvariant violation. Always read *which* line fired: it must be\n`Error: Invariant \u003Cname> is violated`. A `TLC threw an unexpected exception` or\n`Attempted to apply the operator` is a broken mutant, and proves nothing.\n\nAlso: `go run .\u002Fhack\u002Fformal\u002Flint\u002Fmain` runs first and catches unassigned variables and\nconjoined assignments, the two defects that produced false passes in the `\u002Fgoal`\nand `\u002Frun` models.\n\n## After confirming the model is still true\n\n- **Sync commit:** `cc79aceb43df31012e9e5c1f69a7ba1231c32470`\n- **Constants:** none. `MaxCompletedAgents` was modelled as the eviction decision\n  it produces rather than a counter, because a counter made the state space\n  explode past 2 million.\n- **Chart budget:** 35 states, so the full chart always draws.\n\nMove the sync commit forward only after confirming the model describes the code at\nthe new commit.\n\n## Draw the chart\n\n`hack\u002Fformal\u002Fcheck.sh agent --chart` writes `hack\u002Fformal\u002Fagent\u002Fout\u002Freport.md` and\n`chart.mmd` via the pstack `\u002Fstatechart` skill.\n","# Verify the subagent lifecycle\n\nOne subagent has six states, three writers, and a pool slot that must be released\nexactly once. `pool.Release` is an unguarded channel receive\n(`internal\u002Fsubagent\u002Fpool.go:36-38`), so a second release panics the process, and\nnothing in a single-agent test can produce the interleaving that does it.\n\nThe properties: **a cancel is never overwritten by the process's own exit\nreason**, **an agent that leaves the list has already published its verdict**, and\n**exactly one pool slot is released per spawn**.\n\n## Does my change touch the verified code\n\n```bash\ngit diff --stat \u003Csync-commit>..HEAD -- \\\n  internal\u002Fsubagent\u002Forchestrator.go internal\u002Fsubagent\u002Fpool.go internal\u002Fsubagent\u002Ftypes.go\n```\n\n**Model covers** (each model step names the function it stands for):\n\n| Model action | Code |\n|---|---|\n| `SlotAcquired` | `Spawn` past `pool.Acquire`, `orchestrator.go:487-499` |\n| `SpawnRefused` | `trackAgent` refuses after shutdown → `abandonSpawn`, `orchestrator.go:499-505, 599-607` |\n| `Event` \u002F `ProcExits` | the event loop and `proc.Wait`, `orchestrator.go:610-629` |\n| `Cancel` | `Cancel` + `validateAgentForCancel`, `orchestrator.go:806-834` |\n| `ShutdownRunning` \u002F `ShutdownIdle` | `ShutdownWithTimeout`, `orchestrator.go:859-894` |\n| `PublishKept` \u002F `PublishEvicted` | `forwardAgentEvents`' tail: evict, publish, release, `orchestrator.go:628-648, 683-711` |\n\n**Not modelled, and why:** the worktree manager (its own stash lifecycle, modelled\nseparately if it becomes a target); `spawner.go`'s event channel and its silent\ndrops when the 64-slot buffer fills; `terminalStatus`'s signal classification\n(the model takes the outcome as a parameter); `recentTasks` and the prune loop;\nconcurrency above one agent; `Get`\u002F`List` as anything other than a read. A bug in\nany of those is invisible here.\n\n## Run it\n\n```bash\nhack\u002Fformal\u002Fcheck.sh agent           # all rules hold, or the first one that breaks\nhack\u002Fformal\u002Fcheck.sh agent --chart\n```\n\nCost: 35 distinct states, under a second. Exit `0` all rules hold, `1` a rule\nbroke, `2` TLC or the environment failed.\n\n## Rules, and the harm each one names\n\n| Rule | The promise | What breaking it means for a user |\n|---|---|---|\n| `CancelIsSticky` | a cancel request wins over the process exit reason | you press cancel, and pi reports the agent failed instead |\n| `PublishedIsTerminal` | `run_done` never says `running` | a consumer waits forever for a verdict already sent |\n| `EvictionStillPublishes` | an agent missing from the list has published | an agent disappears with no status: neither running nor done |\n| `SlotReleasedOnce` | at most one `pool.Release` per spawn, and only if acquired | `pool.Release` on an empty semaphore panics the process |\n| `NoLateCancel` | a finished agent is never relabeled `canceled` | a completed result is reported as canceled |\n\n## Two traps, both found while building this model\n\n**A rule phrased on the state the bug overwrites cannot see the bug.** The first\n`CancelIsSticky` read `status = \"canceled\" => ...`, which is trivially true once a\nlater step overwrites `status` with `\"failed\"`. The mutation that drops the\n`if status == \"running\"` guard at `orchestrator.go:626` passed cleanly, on 361\nexplored states. The fix is the `cancelAsked` variable: track the *request*, and\nstate the rule about what a consumer reads after it. Re-run the mutation after any\nedit to this rule.\n\n**A mutant that does not compile is not a caught regression.** Two attempted\nmutations \"failed\" because they produced a `null` variable — a TLA+ error, not an\ninvariant violation. Always read *which* line fired: it must be\n`Error: Invariant \u003Cname> is violated`. A `TLC threw an unexpected exception` or\n`Attempted to apply the operator` is a broken mutant, and proves nothing.\n\nAlso: `go run .\u002Fhack\u002Fformal\u002Flint\u002Fmain` runs first and catches unassigned variables and\nconjoined assignments, the two defects that produced false passes in the `\u002Fgoal`\nand `\u002Frun` models.\n\n## After confirming the model is still true\n\n- **Sync commit:** `cc79aceb43df31012e9e5c1f69a7ba1231c32470`\n- **Constants:** none. `MaxCompletedAgents` was modelled as the eviction decision\n  it produces rather than a counter, because a counter made the state space\n  explode past 2 million.\n- **Chart budget:** 35 states, so the full chart always draws.\n\nMove the sync commit forward only after confirming the model describes the code at\nthe new commit.\n\n## Draw the chart\n\n`hack\u002Fformal\u002Fcheck.sh agent --chart` writes `hack\u002Fformal\u002Fagent\u002Fout\u002Freport.md` and\n`chart.mmd` via the pstack `\u002Fstatechart` skill.\n",{"name":5,"description":7},"main","29fdab7a38eef7b4e1ef4101155eb2708f86fa5d","internal\u002Fextension\u002Fbundled_skills\u002Fverify-agent-lifecycle",34,"https:\u002F\u002Fgithub.com\u002Fdimetron\u002Fpi-go","https:\u002F\u002Fpi-go.sh","https:\u002F\u002Fgithub.com\u002Fdimetron","🎸💻 🚧🦞","2026-11-12 06:53:11",{"source":8,"skillName":5,"commands":59,"prompt":69,"zipAvailable":70},[60,63,66],{"tool":61,"command":62},"npx","npx skills add dimetron\u002Fpi-go --skill verify-agent-lifecycle",{"tool":64,"command":65},"bunx","bunx skills add dimetron\u002Fpi-go --skill verify-agent-lifecycle",{"tool":67,"command":68},"pnpm","pnpm dlx skills add dimetron\u002Fpi-go --skill verify-agent-lifecycle","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdimetron\u002Fpi-go\u002Fblob\u002Fmain\u002Finternal\u002Fextension\u002Fbundled_skills\u002Fverify-agent-lifecycle\u002FSKILL.md",true,[],[73,96,118,137,152,166],{"skillNo":74,"name":75,"nameZh":6,"description":76,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":77,"categories":78,"keywords":80,"publishedAt":44},"01M70SJ3216A0Y2NXYJJ2MVER0","verify-session-flow","Formally verify the interactive session flow before or after changing it. Use when editing internal\u002Ftui\u002Ftui.go's handleKey, handleInterruptKey, handleInputSubmit, ctrlCCount or quitting, internal\u002Ftui\u002Fagent_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.","https:\u002F\u002Fgithub.com\u002Fdimetron\u002Fpi-go\u002Fblob\u002Fmain\u002Finternal\u002Fextension\u002Fbundled_skills\u002Fverify-session-flow\u002FSKILL.md",[79],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[81,82,26,83,84,85,86,42,87,41,88,89,30,90,40,91,92,93,94,95],"session","verify session flow","flow","tui","clear","submit","init","loop","spinner","prompt","interactive","reports","first","goal","CTRL",{"skillNo":97,"name":98,"nameZh":6,"description":99,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":102,"publishedAt":44},"01M70SJ02CTB40V2VFGAF80SBG","verify-run-pipeline","Formally verify the \u002Frun phase machine before or after changing it. Use when editing internal\u002Ftui\u002Frun.go's phase transitions (handleRunAgentDone, runGates result handling, verifyRunComplete, retryRun, reportGateHang, merge), internal\u002Fsop\u002Fverdict.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.","https:\u002F\u002Fgithub.com\u002Fdimetron\u002Fpi-go\u002Fblob\u002Fmain\u002Finternal\u002Fextension\u002Fbundled_skills\u002Fverify-run-pipeline\u002FSKILL.md",[],[103,104,26,105,106,107,108,109,110,35,111,112,113,40,41,114,115,116,42,117],"run pipeline","run","verify run pipeline","pipeline","merge","machine","result","transitions","work","refresh","phase","sop","run phase","retry","cycle",{"skillNo":119,"name":120,"nameZh":6,"description":121,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":125,"publishedAt":44},"01M70SHWJV9N3PQWRAMCQE9C2E","verify-history-rewrite","Formally verify the session-history rewrite path before or after changing it. Use when editing internal\u002Fsession\u002Fautocompact.go, internal\u002Fsession\u002Fcompaction*.go, internal\u002Fsession\u002Fstore.go's Compact\u002FClearEvents\u002FrewriteEvents, internal\u002Fagent\u002Fagent.go's Run\u002FRunStreaming\u002FrunPreTurn, or internal\u002Ftui\u002Fcommands.go's clearConversation\u002FclearSessionContext 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.","https:\u002F\u002Fgithub.com\u002Fdimetron\u002Fpi-go\u002Fblob\u002Fmain\u002Finternal\u002Fextension\u002Fbundled_skills\u002Fverify-history-rewrite\u002FSKILL.md",[124],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[26,126,127,128,129,130,131,42,81,132,41,133,40,134,135,109,104,136,30,84],"verify history rewrite","rewrite","history","tool","get","model","call","compact","store","gauge","commands",{"skillNo":138,"name":139,"nameZh":6,"description":140,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":141,"categories":142,"keywords":144,"publishedAt":44},"01M70SHSJ5A2XV5SXBXM96RH7P","verify-goal-loop","Formally verify the \u002Fgoal automatic loop before or after changing it. Use when editing internal\u002Ftui\u002Fgoal.go, internal\u002Ftui\u002Fagent_loop.go's handleAgentDone\u002FcancelAgent\u002FsteerPrompt, or internal\u002Ftools\u002Fgoal.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.","https:\u002F\u002Fgithub.com\u002Fdimetron\u002Fpi-go\u002Fblob\u002Fmain\u002Finternal\u002Fextension\u002Fbundled_skills\u002Fverify-goal-loop\u002FSKILL.md",[143],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[145,146,26,94,88,147,42,117,41,40,148,149,30,84,150,29,151],"verify goal loop","verify goal","goal loop","you","automatic","Tools","stop",{"skillNo":153,"name":154,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":161,"publishedAt":44},"01M6E37WZM8ZF36DRYQGZDGP7M","test-new-skill","https:\u002F\u002Fgithub.com\u002Fdimetron\u002Fpi-go\u002Fblob\u002Fmain\u002Finternal\u002Ftui\u002F.pi-go\u002Fskills\u002Ftest-new-skill\u002FSKILL.md",[157,160],{"categoryNo":158,"categoryName":159,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[162,163,164,165],"test","new skill","test new skill","new",{"skillNo":167,"name":168,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":171,"publishedAt":44},"01M6E37T9PQ6Q8NGVGFHR7QDFN","ponytail","https:\u002F\u002Fgithub.com\u002Fdimetron\u002Fpi-go\u002Fblob\u002Fmain\u002Finternal\u002Fextension\u002Fbundled_skills\u002Fponytail\u002FSKILL.md",[],[168]]