Skip to main content

state-persistence

Provides patterns for reading, writing, and recovering AgLoop state from state.json and plan.yaml. Includes checkpoint management and corruption recovery. Use when implementing state management logic or hook scripts.

来源信息

仓库
zebbern/termstack
最近来源活动
2026年4月10日 22:53
检测到的 SKILL.md 语言
英语
星标
4
Forks
3

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
state-persistence
description
Provides patterns for reading, writing, and recovering AgLoop state from state.json and plan.yaml. Includes checkpoint management and corruption recovery. Use when implementing state management logic or hook scripts.
disable-model-invocation
true
argument-hint
State operation to perform (read, write, checkpoint, recover)
tags
["agloop","state","json","checkpoint","recovery","persistence"]
# State Persistence ## When to Use This Skill Load this skill when: - Reading or writing `.agloop/state.json` (any agent, hook scripts) - Implementing hook scripts that interact with state files - Creating or restoring checkpoints (pre-compact hook, session-start hook) - Recovering from corrupted or missing state - Appending entries to `.agloop/log.json` - Reading or validating `.agloop/plan.yaml` ## state.json Schema Reference The single source of truth for the agentic loop's runtime state. Every agent reads it before acting. Only the coordinator mutates it. ### Field Reference | Field | Type | Required | Default | Constraints | | ----------------------------- | ------------ | -------- | ------------------- | ----------------------------------------------------------------------- | | `$schema` | string | yes | `"agloop-state-v1"` | Literal value | | `feature_name` | string | yes | `""` | Non-empty after init, max 500 chars | | `feature_description` | string | no | `""` | Set after research phase | | `complexity_level` | enum | yes | `"medium"` | `simple \| medium \| complex`. Set during init based on feature scope | | `current_phase` | enum | yes | `"init"` | `init \| research \| plan \| critique \| execute \| verify \| complete` | | `iteration` | number | yes | `0` | 0 ≤ n ≤ 3 | | `current_task_id` | string\|null | yes | `null` | Must reference existing task or null | | `tasks` | array | yes | `[]` | Populated after plan phase | | `tasks[].id` | string | yes | — | Format: `task-NNN` | | `tasks[].title` | string | yes | — | Deliverable-focused | | `tasks[].description` | string | yes | — | Detailed requirements | | `tasks[].status` | enum | yes | `"pending"` | `pending \| in_progress \| done \| failed` | | `tasks[].depends_on` | string[] | yes | `[]` | All referenced IDs must exist | | `tasks[].acceptance_criteria` | string[] | yes | — | ≥1 criterion per task | | `tasks[].failure_modes` | object[] | yes | `[]` | Each: mode, likelihood, impact, mitigation | | `tasks[].result` | object\|null | yes | `null` | Set by executor | | `tasks[].verification` | object\|null | yes | `null` | Set by verifier | | `stop_hook_active` | boolean | yes | `false` | Prevents infinite stop-hook loops | | `last_action` | object | yes | — | Updated on every agent action | | `last_action.agent` | string | yes | — | Which agent performed the action | | `last_action.action` | string | yes | — | What was done | | `last_action.timestamp` | string | yes | — | ISO 8601 | | `last_action.task_id` | string\|null | yes | — | Related task or null | | `phase_history` | array | yes | `[]` | Append-only phase transitions | | `retry_count` | object | yes | `{}` | Keys: task IDs, values: integers ≥ 0 | | `previous_objections` | array | yes | `[]` | Critic objections from plan↔critique loop. Each: `{ iteration, objection, response, resolved, objection_category, severity }`. Reset on new plan phase | | `compaction_context` | object | yes | — | Recovery context for compaction survival. Sub-fields: `last_delegation`, `last_result_summary`, `research_digest`, `plan_digest`, `critique_digest`, `pending_decision`, `compaction_count` | ### Task Result Schema Set by the executor after task completion: ```json { "files_modified": ["src/api/search.ts", "src/api/search.test.ts"], "files_deleted": [], "acceptance_criteria_confirmation": [ { "criterion": "GET /api/search returns 200 with results array", "met": true, "evidence": "Handler returns res.json({ data: results, meta: { total, page }, error: null })" } ], "summary": "Implemented search endpoint with pagination and filtering", "warnings": [], "verified": false } ``` ### Task Verification Schema Set by the verifier after task verification: ```json { "verdict": "PASS", "criteria_results": [ { "criterion": "GET /api/search returns 200 with results array", "result": "pass", "evidence": "Response: { results: [...], total: 42, page: 1 }" } ], "specification_compliance": { "required_patterns": { "compliant": true, "violations": [] }, "forbidden_patterns": { "compliant": true, "violations": [] }, "tech_choices": { "compliant": true, "violations": [] } }, "overall_summary": "All acceptance criteria met, specification compliant" } ``` On FAIL, the verifier adds a `failure_details` field with specific failure information. ``` ## Read-Modify-Write Pattern **Every state mutation must follow this exact pattern.** Never read and write without validation. ### Step-by-Step Procedure ```javascript // 1. READ — Load current state from disk const raw = await fs.readFile(".agloop/state.json", "utf-8"); // 2. PARSE — Convert to object with error handling let state; try { state = JSON.parse(raw); } catch (err) { // State corrupted — attempt checkpoint recovery return await recoverFromCheckpoint(workspaceFolder); } // 3. VALIDATE — Confirm state structure before using if (!state.$schema || state.$schema !== "agloop-state-v1") { throw new Error("Invalid state schema version"); } if ( ![ "init", "research", "plan", "critique", "execute", "verify", "complete", ].includes(state.current_phase) ) { throw new Error(`Invalid phase: ${state.current_phase}`); } // 4. MODIFY — Apply changes to the in-memory object state.current_phase = "execute"; state.current_task_id = "task-001"; state.last_action = { agent: "agloop", action: "phase_transition", timestamp: new Date().toISOString(), task_id: "task-001", }; state.phase_history.push({ phase: "execute", entered_at: new Date().toISOString(), exited_at: null, }); // 5. VALIDATE AGAIN — Confirm changes are consistent if (state.current_task_id) { const taskExists = state.tasks.some((t) => t.id === state.current_task_id); if (!taskExists) throw new Error(`Task ${state.current_task_id} not found`); } // 6. WRITE ATOMICALLY — Write to temp file, then rename const tmpPath = ".agloop/state.json.tmp"; await fs.writeFile(tmpPath, JSON.stringify(state, null, 2), "utf-8"); await fs.rename(tmpPath, ".agloop/state.json"); ``` ### Critical Rules - **Never write without reading first** — always base writes on current disk state - **Double-validate** — validate before modification AND before writing - **Atomic writes** — write to `.tmp` file then rename to prevent corruption on crash - **No partial updates** — always write the complete state object ## plan.yaml Schema Reference Produced by the Planner agent, consumed by Coordinator, Critic, Executor, and Verifier. ### Field Reference | Field | Type | Required | Constraints | | ----------------------------------- | ------ | -------- | --------------------------------------------------------------- | | `plan_id` | string | yes | Format: `plan-YYYYMMDD-HHmmss` | | `feature` | string | yes | Non-empty, verbatim user request | | `created_at` | string | yes | ISO 8601 | | `updated_at` | string | yes | ISO 8601, ≥ created_at | | `status` | enum | yes | `draft \| under_review \| approved \| in_progress \| completed` | | `pre_mortem.overall_risk_level` | enum | yes | `low \| medium \| high \| critical` | | `pre_mortem.critical_failure_modes` | array | yes | ≥1 entry | | `pre_mortem.assumptions` | array | yes | ≥1 entry | | `tasks` | array | yes | ≥1 task | | `tasks[].id` | string | yes | Format: `task-NNN`, unique | | `tasks[].depends_on` | array | yes | All referenced IDs must exist | | `tasks[].acceptance_criteria` | array | yes | ≥1 criterion per task | | `tasks[].estimated_effort` | enum | yes | `small \| medium \| large` | | `dag_edges` | array | yes | Each edge: `[from_id, to_id]`, both must exist | | `critique_history` | array | yes | Empty initially | ## log.json Append Pattern The execution log uses **JSON Lines format** — one JSON object per line, no surrounding array. This enables efficient appending without reading the entire file. ### Append Procedure ```javascript // CORRECT: Append a single line to log.json (JSON Lines format) async function appendLog(logPath, entry) { // Ensure required fields const logEntry = { timestamp: entry.timestamp || new Date().toISOString(), agent: entry.agent, action: entry.action, task_id: entry.task_id || null, phase: entry.phase, input_summary: truncate(entry.input_summary || "", 500), output_summary: truncate(entry.output_summary || "", 500), status: entry.status || "success", ...(entry.metadata ? { metadata: entry.metadata } : {}), }; // Append as a single line — do NOT overwrite const line = JSON.stringify(logEntry) + "\n"; await fs.appendFile(logPath, line, "utf-8"); } // WRONG: Do NOT do this — it reads and rewrites the entire file // const log = JSON.parse(await fs.readFile(logPath)); // log.push(entry); // await fs.writeFile(logPath, JSON.stringify(log)); ``` ### Log Entry Fields | Field | Type | Required | Constraints | | ---------------- | ------------ | -------- | ------------------------------- | | `timestamp` | string | yes | ISO 8601 with milliseconds | | `agent` | string | yes | Agent or hook identifier | | `action` | string | yes | One of the defined action types | | `task_id` | string\|null | yes | Existing task ID or null | | `phase` | string | yes | Current phase name | | `input_summary` | string | yes | Max 500 chars | | `output_summary` | string | yes | Max 500 chars |
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看