452 lines
19 KiB
Markdown
452 lines
19 KiB
Markdown
# Goal-Driven Loop Skill — Design
|
||
|
||
**Date:** 2026-06-10
|
||
**Status:** Approved
|
||
**Target:** Pi coding agent (Agent Skills standard + Extension)
|
||
|
||
## Purpose
|
||
|
||
A goal-driven loop skill + extension pair for Pi that keeps the current session focused on a user-defined goal with explicit acceptance criteria, iterating until all criteria pass or genuine stagnation is detected. The skill is the instruction layer; the extension is the automation layer that eliminates unnecessary user interaction.
|
||
|
||
## Problem
|
||
|
||
Long-running coding sessions drift. The user states a goal, the agent works on it, gets sidetracked by intermediate questions, and loses sight of the original target. When multiple turns span context compaction, the original goal may be forgotten entirely. Pure skill-only designs force user interaction at every checkpoint (stale state, confirmation, stagnation, criteria modification), which suspends the loop and breaks the "set and forget" intent.
|
||
|
||
## Solution
|
||
|
||
Two-layer architecture:
|
||
|
||
1. **Skill (`goal-driven-loop/SKILL.md`)** — instruction layer. Tells the agent what to do when a goal is active: criteria generation, iteration steps, verification, exit flow.
|
||
2. **Extension (`goal-driven-loop/index.ts`)** — automation layer. Detects goal-setting phrases via the `input` event, injects goal state into context via `before_agent_start`, performs mechanical checks (context usage, stale state), and replaces infinite-wait confirms with timed auto-defaults.
|
||
|
||
The extension eliminates the most common causes of user-interaction hang-ups while preserving safety valves (explicit abandon, criteria modification).
|
||
|
||
## Scope
|
||
|
||
**In scope:**
|
||
- A Pi skill named `goal-driven-loop` (`SKILL.md` + `references/error-handling.md`)
|
||
- A Pi extension `goal-driven-loop/index.ts` that automates the loop
|
||
- Trigger detection (Chinese, English, other language variants) via extension `input` event
|
||
- Anti-trigger rules (quotes, trivial tasks, re-entry) via extension
|
||
- Goal state file `.agents/goal/GOAL.md` with frontmatter + criteria + progress log
|
||
- Auto-generation of acceptance criteria when not provided
|
||
- Per-iteration retry with exponential backoff (10 retries, ~37min coverage)
|
||
- Granularity detection that delegates coarse goals to comet
|
||
- Deslop pass and clean exit
|
||
- Extension-driven state injection (resists context compaction)
|
||
|
||
**Out of scope:**
|
||
- Tools for editing/running code (agent uses existing Pi tools)
|
||
- Comet skill implementation (already exists, only referenced)
|
||
- Cross-session goal tracking beyond file persistence
|
||
- Multi-goal concurrent tracking (only one active goal at a time)
|
||
|
||
## Architecture: Skill + Extension
|
||
|
||
```
|
||
User message: "设定当前目标为..."
|
||
↓
|
||
[Extension: input event] detects trigger
|
||
↓
|
||
[Extension: before_agent_start] injects goal state + skill into context
|
||
↓
|
||
[Skill: SKILL.md] instructs agent on loop steps
|
||
↓
|
||
[Agent] runs iteration, updates GOAL.md
|
||
↓
|
||
[Extension: before_agent_start] on next turn re-injects state
|
||
↓
|
||
[Skill: completion] deslop + cleanup + output summary
|
||
```
|
||
|
||
The extension handles all mechanical concerns (detection, injection, context checks, auto-default confirmations). The skill handles all judgment-based concerns (criteria quality, iteration work, verification, exit flow). This separation lets the agent focus on substantive work while the extension guarantees the loop never gets stuck waiting.
|
||
|
||
## Design
|
||
|
||
### 1. Skill: Trigger and Anti-Trigger
|
||
|
||
**Trigger phrases (extension detects via `input` event):**
|
||
- Chinese: 设定当前目标为 / 当前目标是 / 把当前目标设为 / 我的目标是
|
||
- English: set current goal to / current goal is / my goal is / the goal is
|
||
- Other: semantically equivalent phrasings + concrete deliverable
|
||
|
||
**Uncertainty rule:** If uncertain whether a phrase is a trigger, check for: agent-internal-commitment language + concrete deliverable description. If both are present, treat as a trigger.
|
||
|
||
The phrase must be followed by a concrete goal description (non-empty, not a short fragment).
|
||
|
||
**Anti-trigger conditions (extension checks mechanically):**
|
||
|
||
1. **Mechanical quote detection:**
|
||
- Trigger phrase inside markdown quote block (`>`)
|
||
- Trigger phrase inside code block (```)
|
||
- Trigger phrase in same sentence as reported speech markers (he said / she said / they said)
|
||
|
||
2. **Trivial task red flags (any one triggers):**
|
||
- Message contains keywords: hotfix, tweak, fix a bug, small change, quick fix, 修个bug, 小改
|
||
- Goal description ≤ 10 chars AND no architecture/module/system/refactor/integration keywords
|
||
- Agent estimates < 5 min AND goal description contains no architectural keywords
|
||
|
||
3. **Re-entry:** If `.agents/goal/GOAL.md` already exists, see Section 7 (extension behavior).
|
||
|
||
**Granularity split:**
|
||
|
||
| Signal | Fine-grained (this skill) | Coarse-grained (delegate to comet) |
|
||
|--------|---------------------------|-------------------------------------|
|
||
| Estimated file count | ≤ 5 | > 5 |
|
||
| Cross-module coordination | Single concern | Multi-module |
|
||
| Design decisions | None or minor | Architectural |
|
||
| Single-session completion | Likely possible | Likely impossible |
|
||
|
||
Coarse-grained goal → extension automatically invokes `comet-open` with the goal as input. This skill exits.
|
||
|
||
**Fallback:** If comet unavailable (project has no openspec config), downgrade to multi-stage mode — auto-split goal into phases, verify each phase.
|
||
|
||
### 2. Skill: Goal State File
|
||
|
||
**Path:** `.agents/goal/GOAL.md`
|
||
|
||
**Format:**
|
||
|
||
```markdown
|
||
---
|
||
goal: "<goal text>"
|
||
created: "<ISO 8601 timestamp>"
|
||
updated: "<ISO 8601 timestamp>"
|
||
iteration: <int>
|
||
status: in-progress | blocked | complete
|
||
---
|
||
|
||
# Goal: <goal text>
|
||
|
||
## Acceptance Criteria
|
||
- [ ] <criterion 1>
|
||
- [ ] <criterion 2>
|
||
- [ ] <criterion 3>
|
||
|
||
## Progress Log
|
||
### Iteration 1
|
||
- <work done>
|
||
- <verification results>
|
||
|
||
## Blocked
|
||
- <blocker description, or delete section if empty>
|
||
```
|
||
|
||
**Lifecycle:**
|
||
1. Created when trigger fires and goal is fine-grained
|
||
2. Updated after every iteration (criteria checkboxes, progress log, iteration counter, updated timestamp)
|
||
3. Set to `blocked` when 10 retries exhausted or stagnation detected
|
||
4. Set to `complete` when all criteria pass
|
||
5. Deleted after deslop pass at exit
|
||
|
||
### 3. Skill: Error Handling and Interruption
|
||
|
||
**Retry strategy (automatic, no human intervention):**
|
||
|
||
| Parameter | Value |
|
||
|-----------|-------|
|
||
| Max retries per iteration | 10 |
|
||
| Initial delay | 30s |
|
||
| Backoff factor | ×2 |
|
||
| Max delay | 300s (5min) |
|
||
|
||
**Backoff sequence:**
|
||
|
||
| Retry | Delay | Cumulative |
|
||
|-------|-------|------------|
|
||
| 1 | 30s | 0.5min |
|
||
| 2 | 60s | 1.5min |
|
||
| 3 | 120s | 3.5min |
|
||
| 4 | 240s | 7.5min |
|
||
| 5 | 300s | 12.5min |
|
||
| 6 | 300s | 17.5min |
|
||
| 7 | 300s | 22.5min |
|
||
| 8 | 300s | 27.5min |
|
||
| 9 | 300s | 32.5min |
|
||
| 10 | 300s | 37.5min |
|
||
|
||
**Execution:** Skill instructs agent to run `bash -c "sleep <delay> && echo done"` with `timeout = delay + 30`. If sleep is interrupted, treat as tool failure, enter retry.
|
||
|
||
**Retry counter scope:** Per-iteration. Resets at the start of each new iteration.
|
||
|
||
**After 10 retries exhausted:** `status = blocked`, write Blocked section, **extension automatically injects** a brief blocker summary and auto-attempts alternative approach (see Section 7). If still blocked after auto-attempt, then notify user via UI.
|
||
|
||
**Exception classification:**
|
||
|
||
| Exception | Detection signal | Recoverable? | Response |
|
||
|-----------|------------------|--------------|----------|
|
||
| Tool execution failure | bash returns non-zero exit | Yes | Exponential backoff retry |
|
||
| LLM API exception | subagent timeout/error | Yes | Exponential backoff retry |
|
||
| Context tension | `ctx.getContextUsage() > 0.8` of context window | No | Extension saves state, injects continuation message on next turn |
|
||
| User interruption | User sends new message mid-loop | No | Extension auto-pauses, responds to user message, resumes goal on next trigger |
|
||
| Stale state | GOAL.md exists with status = in-progress | No | Extension prompts: continue / replace / abandon (timed auto-default: continue) |
|
||
|
||
**Stagnation detection (dual signal — both required):**
|
||
- Acceptance criteria passing count unchanged for 3 consecutive iterations, AND
|
||
- Progress Log last 3 iterations contain no progress keywords (found / fixed / implemented / added / refactored / verified)
|
||
|
||
When triggered, **extension auto-injects** a strategy-change prompt rather than blocking. After 2 auto-strategy-changes still stalled, fall back to user notification.
|
||
|
||
**Continuation prompt format (fixed):**
|
||
|
||
```
|
||
🔄 目标状态已保存至 .agents/goal/GOAL.md
|
||
当前进度:X/Y criteria 通过,第 N 轮迭代。
|
||
```
|
||
|
||
**Red flags — these do NOT count as completion:**
|
||
|
||
- "LLM call failed, goal should be considered complete" → retry or blocked
|
||
- "Tests don't run but code looks fine" → must have test evidence
|
||
- "Context full, let me summarize" → save state, extension auto-injects continuation
|
||
- "Too many retries, skip this criterion" → exhausted retries → blocked, do not skip
|
||
- "This criterion is too hard to verify, skip it" → requires criteria modification path (user or auto-suggestion)
|
||
|
||
**Explicit abandon:** User says "abandon / don't want it / cancel" → extension deletes `GOAL.md`, outputs brief summary, terminates loop.
|
||
|
||
**Criteria modification:** Agent may suggest criteria modifications. Two paths:
|
||
- **Auto-modify safe adjustments** (e.g., "fix typo in criterion wording", "split compound criterion into two") — agent may apply, log the change
|
||
- **Material change** (e.g., "remove criterion", "lower acceptance bar") — extension prompts user with timed auto-default: keep current
|
||
|
||
### 4. Skill: Core Loop Flow
|
||
|
||
**Step 1: Trigger and routing** (handled by extension `input` event)
|
||
- Extension detects trigger phrase
|
||
- Extension performs anti-trigger check
|
||
- Extension performs granularity check
|
||
- Coarse → extension invokes `comet-open`; fine → continue
|
||
|
||
**Step 2: Initialize goal state** (extension + agent)
|
||
- Extension checks `GOAL.md` existence (stale state)
|
||
- If stale: extension prompts user with timed auto-default (continue)
|
||
- If user provided criteria → use directly
|
||
- If not → auto-generate (verifiability red line below), no confirmation needed unless material uncertainty
|
||
|
||
**Acceptance criteria verifiability red line:**
|
||
- ❌ "Implement rate limiting" → not verifiable
|
||
- ✅ "Rate limiter middleware mounts on auth routes, excess requests return 429" → verifiable
|
||
- ❌ "Code quality is good" → not verifiable
|
||
- ✅ "Related files lint clean, typecheck passes" → verifiable
|
||
|
||
**Step 3: Execute iteration**
|
||
1. Read `GOAL.md`, identify unpassed criteria
|
||
2. Pick the criterion whose completion does not require other unpassed criteria to be met first
|
||
3. Implement work for that criterion
|
||
4. Run relevant verification (tests, lint, typecheck)
|
||
5. Record verification results in Progress Log
|
||
|
||
**Step 4: Verify and update**
|
||
- Check each criterion against fresh evidence
|
||
- Update checkboxes, `iteration` +1, `updated` timestamp
|
||
|
||
**Step 5: Completion check**
|
||
- All criteria ✅ → Step 6 (exit)
|
||
- Some unpassed → stagnation check, then loop back to Step 3
|
||
|
||
**Step 6: Exit**
|
||
1. Deslop pass on modified files
|
||
2. Deslop failure → does not block, note in summary
|
||
3. Delete `GOAL.md`
|
||
4. Output completion summary
|
||
|
||
**Mid-loop granularity upgrade:** If execution reveals the goal is more complex than estimated, extension automatically invokes `comet-open` with current `GOAL.md` state. User is informed via UI notification, not asked for confirmation.
|
||
|
||
### 5. Skill: Comet Integration
|
||
|
||
- Skill hands off to `comet-open` for coarse-grained goals
|
||
- Skill does not manage goals inside comet cycles
|
||
- Mid-loop upgrade from fine to coarse is supported
|
||
- Extension triggers the hand-off automatically
|
||
|
||
### 6. File Structure
|
||
|
||
```
|
||
.pi/
|
||
├── skills/goal-driven-loop/
|
||
│ ├── SKILL.md # Instruction layer (≤500 words)
|
||
│ └── references/
|
||
│ └── error-handling.md # Detailed retry/recovery reference
|
||
└── extensions/
|
||
└── goal-driven-loop/
|
||
├── index.ts # Extension entry point
|
||
├── trigger-detector.ts # Trigger phrase + anti-trigger logic
|
||
├── state-injector.ts # State injection + context checks
|
||
└── ui-prompts.ts # Timed confirm/select helpers
|
||
```
|
||
|
||
Skill: instructions the agent reads when active.
|
||
Extension: TypeScript module that runs in Pi's runtime, handles mechanical automation.
|
||
|
||
### 7. Extension Behavior (Automation Layer)
|
||
|
||
The extension is the primary mechanism for eliminating user interaction hang-ups. It runs autonomously in Pi's event loop and never blocks the user.
|
||
|
||
#### 7.1 Trigger Detection (`input` event)
|
||
|
||
On every user input:
|
||
|
||
1. Parse the input text
|
||
2. Run anti-trigger checks (Section 1 conditions)
|
||
3. If trigger detected and goal is fine-grained:
|
||
- Read or create `GOAL.md` skeleton
|
||
- Inject the skill into the next turn via `before_agent_start`
|
||
- Auto-parse user message to extract goal text and any criteria
|
||
- If criteria absent, let the skill auto-generate
|
||
4. If trigger detected and goal is coarse:
|
||
- Invoke `comet-open` via `pi.sendUserMessage()` with goal as input
|
||
- Notify user via `ctx.ui.notify()` (non-blocking toast)
|
||
5. If no trigger detected, pass through normally
|
||
|
||
#### 7.2 State Injection (`before_agent_start` event)
|
||
|
||
On every agent start:
|
||
|
||
1. Check if `.agents/goal/GOAL.md` exists
|
||
2. If yes and `status = in-progress`:
|
||
- Read the file
|
||
- Inject summary into the system prompt or as a persistent message
|
||
- Inject format:
|
||
|
||
```
|
||
[goal-driven-loop] ACTIVE GOAL
|
||
{goal text}
|
||
Progress: {X}/{Y} criteria passed, iteration {N}
|
||
Next step: read .agents/goal/GOAL.md and continue iteration
|
||
```
|
||
|
||
3. If `status = blocked`:
|
||
- Inject blocker details
|
||
- Inject "change strategy" prompt
|
||
4. If `status = complete`:
|
||
- Inject completion summary and clear the message
|
||
|
||
This is the key mechanism for context-compaction resistance: even if the conversation is compacted, the extension rebuilds goal context from the file.
|
||
|
||
#### 7.3 Context Tension Detection
|
||
|
||
In `before_agent_start`:
|
||
|
||
1. Call `ctx.getContextUsage()`
|
||
2. If `usage.tokens / contextWindow > 0.8`:
|
||
- Read `GOAL.md` to ensure state is current
|
||
- Inject a "save progress now" directive into the system prompt
|
||
- The directive tells the agent: if this turn's work would lose goal state, finalize `GOAL.md` first, then output the continuation prompt
|
||
|
||
#### 7.4 Stale State Resolution
|
||
|
||
When `GOAL.md` exists with `status = in-progress` and a new trigger is detected:
|
||
|
||
1. Use `ctx.ui.select()` with **timeout: 10000ms**
|
||
2. Options: "继续当前目标" (default) / "替换为新目标" / "放弃当前目标"
|
||
3. After 10s timeout, auto-select "继续"
|
||
4. The user's choice triggers the corresponding skill branch
|
||
|
||
This replaces the "wait forever for user" pattern with a 10-second auto-default.
|
||
|
||
#### 7.5 Criteria Auto-Generation Quality Gate
|
||
|
||
In `before_agent_start`, after skill auto-generates criteria:
|
||
|
||
1. Parse the generated criteria
|
||
2. Flag any criterion that fails the verifiability red line
|
||
3. For flagged criteria, inject a "tighten this criterion" prompt
|
||
4. The agent must rewrite or remove the flagged criterion before writing `GOAL.md`
|
||
|
||
#### 7.6 Stagnation Auto-Recovery
|
||
|
||
When the skill detects stagnation (dual signal from Section 3):
|
||
|
||
1. Extension auto-injects a "change strategy" prompt:
|
||
|
||
```
|
||
Stagnation detected. Current approach not making progress. Try:
|
||
- Different tool or library
|
||
- Refactor before retry
|
||
- Split the failing criterion into sub-steps
|
||
```
|
||
|
||
2. This is delivered via `pi.sendMessage({ deliverAs: "steer" })`
|
||
3. After 2 consecutive stagnation auto-recoveries with no progress, the extension then notifies the user via UI
|
||
|
||
#### 7.7 Mid-Loop Granularity Upgrade
|
||
|
||
When the agent signals (via criteria update or system message) that the goal is more complex than estimated:
|
||
|
||
1. Extension reads current `GOAL.md`
|
||
2. Extension invokes `comet-open` via `pi.sendUserMessage()`
|
||
3. Extension notifies user via `ctx.ui.notify()` (non-blocking)
|
||
4. No user confirmation required
|
||
|
||
#### 7.8 Abandon Detection
|
||
|
||
If user input contains "abandon" / "cancel" / "不要了" / "放弃":
|
||
|
||
1. Extension deletes `.agents/goal/GOAL.md`
|
||
2. Extension injects a brief completion summary
|
||
3. No further user interaction required
|
||
|
||
### 8. User Interaction Inventory
|
||
|
||
After the extension layer is in place, the remaining user interactions are:
|
||
|
||
| Interaction | Trigger | User action needed? | Auto-default? |
|
||
|-------------|---------|---------------------|---------------|
|
||
| Stale state resolution | New trigger, GOAL.md exists | Optional | 10s timeout → continue |
|
||
| Criteria verifiability failure | Auto-gen produces non-verifiable | No (auto-rewrite) | N/A |
|
||
| Stagnation | 3-round stall | No (2x auto-recover) | After 2 recoveries, notify |
|
||
| Mid-loop upgrade | Complexity discovered | No (auto-delegate) | N/A |
|
||
| Explicit abandon | User says "abandon" | Required (must be user) | N/A |
|
||
| Criteria material change | Agent suggests removal/bar-lowering | Optional | Timed auto-default: keep |
|
||
|
||
The user's "set and forget" goal is preserved: they can leave the session, return, and the loop has continued autonomously.
|
||
|
||
## Testing Strategy
|
||
|
||
Following writing-skills TDD approach:
|
||
|
||
### RED Phase — Baseline Behaviors
|
||
|
||
Without the skill + extension:
|
||
- Agent ignores goal-setting phrases (treats as ordinary message)
|
||
- Agent gives up on transient tool failures, asks user
|
||
- Agent loses goal state on context compaction
|
||
- Agent blocks forever on user confirmations
|
||
|
||
### GREEN Phase — Skill + Extension Implementation
|
||
|
||
**Skill layer** (Tasks 1-2):
|
||
- Write `SKILL.md` (≤500 words)
|
||
- Write `references/error-handling.md`
|
||
|
||
**Extension layer** (Tasks 3-7):
|
||
- `trigger-detector.ts` — phrase + anti-trigger parsing
|
||
- `state-injector.ts` — `before_agent_start` state injection
|
||
- `ui-prompts.ts` — timed confirm/select helpers
|
||
- `index.ts` — wiring events together
|
||
- Behavioral scenarios verifying all extension behavior
|
||
|
||
### REFACTOR Phase — Close Loopholes
|
||
|
||
- Test scenarios for each anti-trigger pattern
|
||
- Test that context compaction preserves goal state
|
||
- Test that stale state auto-defaults work
|
||
- Test that stagnation auto-recovery kicks in
|
||
- Test that 2x recovery failure escalates to user
|
||
|
||
## Risks and Mitigations
|
||
|
||
| Risk | Mitigation |
|
||
|------|------------|
|
||
| Agent rationalizes skipping criteria | Red flags list, criteria modification requires logged reason |
|
||
| Sleep timeout in Pi | `bash` timeout = `delay + 30` |
|
||
| Context exhaustion loses goal | Extension injects state on every `before_agent_start`; mechanical 0.8 threshold |
|
||
| Deslop pass fails | Does not block completion, noted in summary |
|
||
| Granularity misclassification | Mid-loop auto-upgrade to comet, no user gate |
|
||
| Re-entry confusion | Stale state uses timed auto-default, no continuation heuristic |
|
||
| Extension bugs block the loop | Extension failures degrade gracefully — skill instructions still work without extension |
|
||
| User loses ability to intervene | Explicit abandon and material criteria change still user-controllable |
|
||
|
||
## Open Questions
|
||
|
||
None at design time. All major decisions resolved during brainstorming.
|