@colin4k1024/tsp 2.5.1 → 2.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+
6
+ const CANGMING_HOME = path.join(os.homedir(), '.config', 'cangming');
7
+ const PLUGIN_DIR = path.join(CANGMING_HOME, 'plugins', 'team-skills-platform');
8
+
9
+ console.log('=== Cangming 安装验证 ===\n');
10
+
11
+ let passed = 0;
12
+ let failed = 0;
13
+
14
+ function check(description, condition) {
15
+ if (condition) {
16
+ console.log(`✅ ${description}`);
17
+ passed++;
18
+ } else {
19
+ console.log(`❌ ${description}`);
20
+ failed++;
21
+ }
22
+ }
23
+
24
+ console.log('📁 目录结构检查:');
25
+ check('CANGMING_HOME 目录存在', fs.existsSync(CANGMING_HOME));
26
+ check('AGENTS.md 文件存在', fs.existsSync(path.join(CANGMING_HOME, 'AGENTS.md')));
27
+ check('agents 目录存在', fs.existsSync(path.join(CANGMING_HOME, 'agents')));
28
+ check('command 目录存在', fs.existsSync(path.join(CANGMING_HOME, 'command')));
29
+ check('plugins 目录存在', fs.existsSync(path.join(CANGMING_HOME, 'plugins')));
30
+ check('team-skills-platform 插件存在', fs.existsSync(PLUGIN_DIR));
31
+
32
+ console.log('\n📄 文件内容检查:');
33
+
34
+ const agentsMdPath = path.join(CANGMING_HOME, 'AGENTS.md');
35
+ if (fs.existsSync(agentsMdPath)) {
36
+ const content = fs.readFileSync(agentsMdPath, 'utf8');
37
+ check('AGENTS.md 包含团队技能平台标记', content.includes('<!-- team-skills-platform -->'));
38
+ check('AGENTS.md 包含角色索引', content.includes('## 可用角色'));
39
+ check('AGENTS.md 包含命令索引', content.includes('## 核心团队命令'));
40
+ check('AGENTS.md 包含插件根路径', content.includes('## 插件根路径'));
41
+ }
42
+
43
+ console.log('\n👥 Agents 检查:');
44
+ const agentsDir = path.join(CANGMING_HOME, 'agents');
45
+ if (fs.existsSync(agentsDir)) {
46
+ const agentFiles = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md'));
47
+ check(`agents 目录包含文件 (${agentFiles.length})`, agentFiles.length > 0);
48
+
49
+ const roleAgents = ['tech-lead.md', 'product-manager.md', 'architect.md', 'frontend-engineer.md',
50
+ 'backend-engineer.md', 'qa-engineer.md', 'devops-engineer.md'];
51
+ for (const agent of roleAgents) {
52
+ check(`角色 agent ${agent} 存在`, fs.existsSync(path.join(agentsDir, agent)));
53
+ }
54
+
55
+ const specialistAgents = agentFiles.filter(f => f.startsWith('specialist-'));
56
+ check(`specialist agents 存在 (${specialistAgents.length})`, specialistAgents.length > 0);
57
+ }
58
+
59
+ console.log('\n📝 Commands 检查:');
60
+ const commandsDir = path.join(CANGMING_HOME, 'command');
61
+ if (fs.existsSync(commandsDir)) {
62
+ const commandFiles = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md'));
63
+ check(`commands 目录包含文件 (${commandFiles.length})`, commandFiles.length > 0);
64
+
65
+ const coreCommands = ['team-intake.md', 'team-plan.md', 'team-execute.md', 'team-review.md',
66
+ 'team-release.md', 'handoff.md'];
67
+ for (const cmd of coreCommands) {
68
+ check(`核心命令 ${cmd} 存在`, fs.existsSync(path.join(commandsDir, cmd)));
69
+ }
70
+ }
71
+
72
+ console.log('\n🎯 Skills 检查:');
73
+ const skillsDir = path.join(PLUGIN_DIR, 'skills');
74
+ if (fs.existsSync(skillsDir)) {
75
+ const skillDirs = fs.readdirSync(skillsDir, { withFileTypes: true })
76
+ .filter(d => d.isDirectory())
77
+ .map(d => d.name);
78
+ check(`skills 目录包含目录 (${skillDirs.length})`, skillDirs.length > 0);
79
+ }
80
+
81
+ console.log('\n📜 Rules 检查:');
82
+ const rulesDir = path.join(PLUGIN_DIR, 'rules');
83
+ if (fs.existsSync(rulesDir)) {
84
+ const ruleItems = fs.readdirSync(rulesDir);
85
+ check(`rules 目录包含内容 (${ruleItems.length})`, ruleItems.length > 0);
86
+ check('common 规则目录存在', fs.existsSync(path.join(rulesDir, 'common')));
87
+ }
88
+
89
+ // 总结
90
+ console.log('\n=== 测试总结 ===');
91
+ console.log(`通过: ${passed}`);
92
+ console.log(`失败: ${failed}`);
93
+ console.log(`总计: ${passed + failed}`);
94
+
95
+ if (failed === 0) {
96
+ console.log('\n🎉 所有测试通过!Cangming 安装成功。');
97
+ console.log('\n下一步:');
98
+ console.log('1. 启动 Cangming: cangming');
99
+ console.log('2. 查看可用角色: AGENTS.md 中包含所有角色索引');
100
+ console.log('3. 执行团队命令: /team-intake, /team-plan 等');
101
+ process.exit(0);
102
+ } else {
103
+ console.log('\n⚠️ 部分测试失败,请检查安装。');
104
+ process.exit(1);
105
+ }
@@ -0,0 +1,150 @@
1
+ ---
2
+ name: goal-convergence
3
+ description: "Goal-oriented autonomous loop with external completion oracle. Keeps iterating until verifiable stopping conditions are met, checked by a separate model."
4
+ origin: ECC
5
+ ---
6
+
7
+ # Goal Convergence
8
+
9
+ Goal-oriented autonomous loop with external completion oracle.
10
+ Based on Addy Osmani's Loop Engineering principle: "Keep going until a verifiable
11
+ stopping condition holds, with a separate small model checking completion."
12
+
13
+ ## When to Activate
14
+
15
+ - User invokes `/goal` command
16
+ - Heartbeat auto-creates a goal from scan failures
17
+ - Triage item is promoted to a goal via `/triage act <id> goal`
18
+ - Session resumes with an active goal state file
19
+
20
+ ## Core Concept: Maker-Oracle Separation
21
+
22
+ The model that wrote the code is too nice grading its own homework.
23
+ A second model (the oracle) with different instructions and READ-ONLY tools
24
+ catches what the first talked itself into.
25
+
26
+ ```
27
+ Maker (primary model, full tools)
28
+
29
+ ▼ produces iteration output
30
+
31
+ Oracle (different model, read-only)
32
+
33
+ ├─ ALL conditions pass → CONVERGE (goal done)
34
+ ├─ Some fail + budget remains → nextHint → LOOP (maker iterates)
35
+ └─ Budget exhausted → ESCALATE (triage inbox)
36
+ ```
37
+
38
+ ## Goal Lifecycle
39
+
40
+ ```
41
+ CREATE → ACTIVE → [iterate] → CONVERGED
42
+
43
+ ├──── PAUSED (manual /goal pause)
44
+ └──── ESCALATED (budget exhausted or repeated failure)
45
+ ```
46
+
47
+ States:
48
+ - `active`: Maker-oracle loop is running
49
+ - `paused`: Manually paused, resumes with `/goal resume`
50
+ - `converged`: All stopping conditions met, goal complete
51
+ - `escalated`: Cannot converge within budget, needs human triage
52
+ - `failed`: Explicitly abandoned
53
+
54
+ ## Stopping Conditions
55
+
56
+ Each goal has one or more stopping conditions. ALL must pass for convergence.
57
+
58
+ | Type | Command Pattern | Example |
59
+ |------|----------------|---------|
60
+ | `test_pass` | Test runner exits 0 | `npm test` |
61
+ | `lint_clean` | Linter exits 0 | `npm run lint -- --quiet` |
62
+ | `coverage_threshold` | Coverage >= N% | `npm test -- --coverage` |
63
+ | `build_pass` | Build succeeds | `npm run build` |
64
+ | `custom_command` | Any command exits 0 | `grep -r "TODO" src/ \| wc -l` |
65
+
66
+ ## Oracle Protocol
67
+
68
+ The oracle receives:
69
+ 1. The goal objective (natural language)
70
+ 2. All stopping condition commands and their latest output
71
+ 3. The maker's iteration summary
72
+ 4. Previous iteration history (last 3)
73
+
74
+ The oracle returns:
75
+ ```json
76
+ {
77
+ "converged": false,
78
+ "conditionResults": [
79
+ {"type": "test_pass", "passed": false, "output": "2 tests failing"},
80
+ {"type": "lint_clean", "passed": true, "output": ""}
81
+ ],
82
+ "reasons": ["2 tests still failing in auth module"],
83
+ "nextHint": "Focus on src/auth/refresh.test.ts — the token expiry mock is stale",
84
+ "confidence": 0.85
85
+ }
86
+ ```
87
+
88
+ ## Budget Management
89
+
90
+ Three budget dimensions, any exhaustion triggers escalation:
91
+
92
+ | Dimension | Default | Rationale |
93
+ |-----------|---------|-----------|
94
+ | Iterations | 15 | Prevents infinite loops |
95
+ | Wall time | 2h | Bounds real-world duration |
96
+ | Cost (USD) | $10 | Prevents runaway API spend |
97
+
98
+ On escalation, the goal:
99
+ 1. Persists full state to `~/.claude/goals/{goalId}.json`
100
+ 2. Creates a triage inbox item with context
101
+ 3. Marks state as `escalated`
102
+ 4. Reports final status to user
103
+
104
+ ## Inter-Session Persistence
105
+
106
+ Goals survive session restarts:
107
+ 1. `SessionEnd` hook serializes active goals
108
+ 2. `SessionStart` hook detects active goals and notifies user
109
+ 3. `/goal resume` re-enters the loop with oracle's last `nextHint`
110
+
111
+ State file: `~/.claude/goals/{goalId}.json` (follows `schemas/goal.schema.json`)
112
+
113
+ ## Integration Points
114
+
115
+ - **`/heartbeat`**: Auto-creates goals from scan failures
116
+ - **`/triage`**: Escalated goals land in triage; triage items can become goals
117
+ - **`/checkpoint`**: Each iteration implicitly checkpoints
118
+ - **`/verify`**: Oracle internally uses verification patterns
119
+ - **`wave-execution`**: Multiple goals can run in parallel waves
120
+ - **`rework-loop`**: Failed iterations trigger blame-attributed rework
121
+
122
+ ## Hard Bans
123
+
124
+ - Oracle MUST be a different model than maker (eliminates self-grading bias)
125
+ - Oracle MUST NOT have write tools (prevents it from "helping" fix issues)
126
+ - Goals MUST have at least one stopping condition (no open-ended loops)
127
+ - Budget MUST have at least one limit set (no unbounded execution)
128
+ - State MUST persist to disk (no goals lost on crash)
129
+
130
+ ## Example
131
+
132
+ ```
133
+ User: /goal "make all tests pass"
134
+
135
+ System infers:
136
+ objective: "make all tests pass"
137
+ stoppingConditions: [{type: "test_pass", command: "npm test"}]
138
+ budget: {maxIterations: 15, maxDuration: "2h", maxDollars: 10}
139
+ oracle: {model: "haiku"}
140
+
141
+ Iteration 1:
142
+ Maker: reads test output, fixes obvious import error in auth.ts
143
+ Oracle: runs `npm test` → 1 test still failing → {converged: false, nextHint: "..."}
144
+
145
+ Iteration 2:
146
+ Maker: fixes token refresh logic based on oracle hint
147
+ Oracle: runs `npm test` → all pass → {converged: true}
148
+
149
+ Result: CONVERGED in 2 iterations, $0.45 spent
150
+ ```
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: loop-heartbeat
3
+ description: "Scheduled discovery automation that runs scans on a heartbeat interval, classifies findings, and routes them to goals or triage inbox."
4
+ origin: ECC
5
+ ---
6
+
7
+ # Loop Heartbeat
8
+
9
+ Scheduled discovery automation that runs scans, finds issues, and routes findings
10
+ to goals or triage. The heartbeat is what makes a loop a loop — not a one-shot run.
11
+
12
+ Based on Addy Osmani's Loop Engineering: "Automations are the thing that makes
13
+ a loop an actual loop. Run prompts on a schedule. Findings go to a triage inbox;
14
+ empty runs archive themselves."
15
+
16
+ ## When to Activate
17
+
18
+ - User invokes `/heartbeat start` to begin scheduled scans
19
+ - User invokes `/heartbeat run` for a one-shot scan
20
+ - A goal converges and follow-up scanning is needed
21
+ - Project onboarding includes quality monitoring setup
22
+
23
+ ## Core Concept
24
+
25
+ The heartbeat is the "discovery layer" of a loop. It answers: "What's broken right
26
+ now?" on a recurring basis without human prompting.
27
+
28
+ ```
29
+ Every 30 minutes:
30
+ ┌─ Run test suite ─── PASS → skip
31
+ ├─ Run linter ─────── FAIL → create goal (auto-fix)
32
+ ├─ Audit deps ─────── FAIL → triage inbox (human decision)
33
+ └─ Type check ─────── PASS → skip
34
+ ```
35
+
36
+ ## Configuration
37
+
38
+ Create `.claude/heartbeat.yaml` in the project root:
39
+
40
+ ```yaml
41
+ heartbeat:
42
+ interval: "30m"
43
+ scans:
44
+ - name: "test-health"
45
+ command: "npm test 2>&1 | tail -10"
46
+ onFailure: "auto-goal"
47
+ description: "Test suite health"
48
+ - name: "lint-drift"
49
+ command: "npm run lint -- --quiet 2>&1 | wc -l"
50
+ threshold: 0
51
+ onFailure: "triage"
52
+ description: "Lint error count"
53
+ - name: "type-check"
54
+ command: "npx tsc --noEmit 2>&1; echo EXIT:$?"
55
+ onFailure: "auto-goal"
56
+ description: "TypeScript type errors"
57
+ budget:
58
+ maxDollarsPerHour: 2.0
59
+ pauseOnExhaust: true
60
+ ```
61
+
62
+ ## Scan Result Classification
63
+
64
+ | onFailure | Behavior | Use When |
65
+ |-----------|----------|----------|
66
+ | `auto-goal` | Creates `/goal` automatically | Machine can fix it (tests, lint, types) |
67
+ | `triage` | Adds to `/triage` inbox | Human judgment needed (deps, security) |
68
+ | `notify` | Desktop notification only | Informational, no action required |
69
+ | `ignore` | Log silently | Monitoring only, aggregate later |
70
+
71
+ ## Budget Controls
72
+
73
+ - `maxDollarsPerHour`: Pause heartbeat if scanning costs exceed this
74
+ - `pauseOnExhaust`: If true, pause (resumable); if false, stop entirely
75
+ - Goal creation inherits the default goal budget from `/goal` settings
76
+
77
+ ## Empty Run Handling
78
+
79
+ When all scans pass:
80
+ - No goals or triage items are created
81
+ - Run is logged to `~/.claude/heartbeat-last-run.json`
82
+ - Next run proceeds on schedule
83
+ - This is the "archive themselves silently" behavior
84
+
85
+ ## Integration Points
86
+
87
+ - **`/goal`**: `auto-goal` failures create goals via completion-oracle.js
88
+ - **`/triage`**: `triage` failures append to `~/.claude/triage/inbox.jsonl`
89
+ - **CronCreate**: Scheduling uses the Claude Code CronCreate primitive
90
+ - **ScheduleWakeup**: Alternative for dynamic-interval loops
91
+ - **Hooks**: Runs as a scheduled task, not a synchronous hook
92
+
93
+ ## Hard Bans
94
+
95
+ - Heartbeat MUST NOT modify code (it's discovery-only)
96
+ - Heartbeat MUST NOT create goals without a stopping condition
97
+ - Budget limits MUST be enforced (no unbounded scan spending)
98
+ - Failed scans MUST be classified (no silent failures)
99
+ - Empty runs MUST NOT create noise (pass = skip)
100
+
101
+ ## Example Session
102
+
103
+ ```
104
+ User: /heartbeat start
105
+
106
+ System:
107
+ Loaded .claude/heartbeat.yaml (3 scans configured)
108
+ Registered CronCreate: every 30m
109
+ Next run: 30m from now
110
+ Budget: $2.00/hour
111
+
112
+ [30 minutes later, heartbeat fires]
113
+
114
+ Scan results:
115
+ ✓ test-health: all tests pass
116
+ ✗ lint-drift: 4 lint errors found → created goal-a1b2c3d4
117
+ ✓ type-check: no type errors
118
+
119
+ Summary: 2/3 scans passed, 1 goal created
120
+ ```
@@ -0,0 +1,132 @@
1
+ ---
2
+ name: mcp-connector-bridge
3
+ description: "Active MCP connector polling that integrates external tools (issue trackers, error monitoring, CI) into the heartbeat loop for cross-system discovery."
4
+ origin: ECC
5
+ ---
6
+
7
+ # MCP Connector Bridge
8
+
9
+ Active MCP connector polling that integrates external tools into the heartbeat loop.
10
+ The difference between an agent that says "here is the fix" vs a loop that opens
11
+ the PR, links the ticket, and pings the channel — by itself.
12
+
13
+ ## When to Activate
14
+
15
+ - User configures `.claude/connectors.yaml` with active MCP servers
16
+ - Heartbeat scheduler includes connector-based scans
17
+ - User invokes `/heartbeat start` with connector scans configured
18
+ - External event triggers a connector poll
19
+
20
+ ## Core Concept
21
+
22
+ MCP connectors let the loop touch real tools: issue trackers, error monitoring,
23
+ databases, CI systems, chat platforms. Without connectors, the loop is isolated
24
+ to the local repo. With connectors, it can discover issues from anywhere.
25
+
26
+ ```
27
+ Heartbeat tick
28
+
29
+ ├─ Local scans (test, lint, type-check)
30
+
31
+ └─ Connector scans (MCP servers)
32
+ ├─ GitHub: "List open issues assigned to me"
33
+ ├─ Sentry: "List unresolved errors in last 24h"
34
+ └─ Linear: "List my in-progress tasks stale >7d"
35
+ ```
36
+
37
+ ## Configuration
38
+
39
+ Create `.claude/connectors.yaml`:
40
+
41
+ ```yaml
42
+ connectors:
43
+ - name: "github-issues"
44
+ server: "github"
45
+ heartbeatScan: "List open issues assigned to me with label:bug"
46
+ triageOnNew: true
47
+ priority: "medium"
48
+
49
+ - name: "sentry-errors"
50
+ server: "sentry"
51
+ heartbeatScan: "List unresolved errors from last 24 hours"
52
+ triageOnNew: true
53
+ priority: "high"
54
+
55
+ - name: "linear-tasks"
56
+ server: "linear"
57
+ heartbeatScan: "List my in-progress tasks not updated in 7 days"
58
+ autoGoalOnStale: "7d"
59
+ priority: "low"
60
+
61
+ - name: "slack-mentions"
62
+ server: "slack"
63
+ heartbeatScan: "Check unread mentions in engineering channel"
64
+ triageOnNew: true
65
+ priority: "low"
66
+ ```
67
+
68
+ ## Connector Scan Fields
69
+
70
+ | Field | Required | Description |
71
+ |-------|----------|-------------|
72
+ | `name` | Yes | Unique connector identifier |
73
+ | `server` | Yes | MCP server name (must be configured in mcp-configs/) |
74
+ | `heartbeatScan` | Yes | Prompt to send to the MCP server |
75
+ | `triageOnNew` | No | Create triage items for new findings |
76
+ | `autoGoalOnStale` | No | Auto-create goal if stale beyond duration |
77
+ | `priority` | No | Default severity for triage items (high/medium/low) |
78
+ | `enabled` | No | Disable without removing (default: true) |
79
+
80
+ ## Health Check
81
+
82
+ Before polling, verify connector is reachable:
83
+
84
+ 1. Check MCP server is configured in project MCP settings
85
+ 2. Verify server responds to basic query
86
+ 3. If unreachable: skip this connector, log warning
87
+ 4. If reachable: execute heartbeatScan prompt
88
+
89
+ Builds on existing `pre:mcp-health-check` hook.
90
+
91
+ ## Result Classification
92
+
93
+ Connector scan results follow the same classification as local scans:
94
+
95
+ | Finding | Action |
96
+ |---------|--------|
97
+ | New issue discovered | → Triage item (if `triageOnNew: true`) |
98
+ | Stale task detected | → Auto-goal (if `autoGoalOnStale` configured) |
99
+ | No new findings | → Skip silently |
100
+ | Connection failed | → Log warning, skip |
101
+
102
+ ## Integration Points
103
+
104
+ - **`/heartbeat`**: Connector scans run alongside local scans during each tick
105
+ - **`/triage`**: New connector findings populate the triage inbox
106
+ - **`/goal`**: Stale task connectors can auto-create goals
107
+ - **`mcp-configs/`**: Connector references must match configured MCP servers
108
+ - **MCP health hook**: Validates connectivity before scan
109
+
110
+ ## Hard Bans
111
+
112
+ - Connectors MUST NOT write to external systems during discovery (read-only scans)
113
+ - Failed connections MUST NOT block local heartbeat scans
114
+ - Connector output MUST be size-limited (prevent token overflow from large responses)
115
+ - Credentials MUST NOT be stored in connectors.yaml (use MCP server config)
116
+ - Polling frequency MUST respect rate limits of external services
117
+
118
+ ## Example Flow
119
+
120
+ ```
121
+ Heartbeat tick (every 30m):
122
+ Local scans: ✓ tests pass, ✓ lint clean
123
+ Connector scans:
124
+ github-issues: Found 2 new bugs → triage inbox
125
+ sentry-errors: No new errors → skip
126
+ linear-tasks: 1 task stale 10 days → auto-goal created
127
+
128
+ Result:
129
+ 2 triage items created from GitHub
130
+ 1 goal created from Linear stale task
131
+ Total: 3 actions from this tick
132
+ ```
@@ -0,0 +1,131 @@
1
+ ---
2
+ name: rework-loop
3
+ description: "Structured rework with git blame attribution for failed goal iterations. Maps failures to responsible changes and routes targeted fix briefs."
4
+ origin: ECC
5
+ ---
6
+
7
+ # Rework Loop
8
+
9
+ Structured rework with blame attribution for failed goal iterations.
10
+ When the oracle says "fail," don't blindly retry — identify what broke,
11
+ map it to the responsible change, and route a targeted fix.
12
+
13
+ ## When to Activate
14
+
15
+ - Oracle returns `fail` verdict during a goal iteration
16
+ - Code reviewer identifies rework-worthy issues
17
+ - Build/test failures after a maker iteration
18
+ - Manual invocation for targeted re-implementation
19
+
20
+ ## Core Concept: Blame-Attributed Rework
21
+
22
+ Blind retry is the #1 waste in autonomous loops. A rework loop instead:
23
+
24
+ 1. **Parses** failure output to extract specific failing locations
25
+ 2. **Blames** via `git diff` / `git blame` to find the responsible change
26
+ 3. **Constrains** the rework to only the affected scope
27
+ 4. **Routes** to the right specialist (TS issues → typescript-reviewer, etc.)
28
+ 5. **Tracks** rework attempts per location to detect persistent trouble spots
29
+
30
+ ```
31
+ Oracle: FAIL
32
+
33
+
34
+ Parse failure → "tests/auth.test.ts:45 — token refresh assertion fails"
35
+
36
+
37
+ Blame → "src/auth/refresh.ts:23 changed in this iteration"
38
+
39
+
40
+ Rework brief → {file: "src/auth/refresh.ts", constraint: "only fix refresh logic"}
41
+
42
+
43
+ Route → backend-engineer or typescript-reviewer
44
+
45
+
46
+ Track → attempts[src/auth/refresh.ts] += 1
47
+ ```
48
+
49
+ ## Rework Brief Format
50
+
51
+ The rework brief is a structured context packet for the next iteration:
52
+
53
+ ```markdown
54
+ ## REWORK BRIEF
55
+
56
+ **Goal:** {original objective}
57
+ **Iteration:** {N} (rework attempt {M} for this location)
58
+
59
+ **Failing evidence:**
60
+ - Test: tests/auth.test.ts:45 — "should refresh expired tokens"
61
+ - Error: Expected token.expiresAt to be > now, got 2024-01-01
62
+
63
+ **Root cause (blame):**
64
+ - File: src/auth/refresh.ts:23
65
+ - Change: Added early return before expiry check
66
+ - Commit: (this iteration, uncommitted)
67
+
68
+ **Constraint:**
69
+ - ONLY modify src/auth/refresh.ts
70
+ - DO NOT touch test files (they define correct behavior)
71
+ - The refresh logic must handle: valid token, expired token, missing token
72
+
73
+ **Suggested fix:**
74
+ - Remove the early return at line 23
75
+ - Ensure expiry check runs before refresh attempt
76
+ ```
77
+
78
+ ## Escalation Triggers
79
+
80
+ Rework escalates to triage when:
81
+
82
+ | Condition | Action |
83
+ |-----------|--------|
84
+ | 3+ rework attempts on same location | Escalate: "persistent trouble spot" |
85
+ | Rework introduces new failures | Escalate: "regression cascade" |
86
+ | Blame maps to multiple unrelated files | Escalate: "scope too broad for targeted rework" |
87
+ | No clear blame (flaky test, env issue) | Escalate: "non-deterministic failure" |
88
+
89
+ ## Blame Attribution Algorithm
90
+
91
+ 1. **Parse failure output** for file paths and line numbers
92
+ 2. **Get changed files** via `git diff --name-only` (this iteration vs last good)
93
+ 3. **Intersect** failing files with changed files
94
+ 4. **For each intersection:**
95
+ - `git diff <file>` to extract specific hunks
96
+ - Map test failure line to source change via call stack or import chain
97
+ 5. **If no intersection:** failure is pre-existing or environmental → escalate
98
+
99
+ ## Rework Tracking
100
+
101
+ Track attempts per file/function to detect patterns:
102
+
103
+ ```json
104
+ {
105
+ "src/auth/refresh.ts": {
106
+ "attempts": 3,
107
+ "lastAttempt": "2024-01-15T10:30:00Z",
108
+ "outcomes": ["fail", "fail", "pass"],
109
+ "totalCost": 1.20
110
+ }
111
+ }
112
+ ```
113
+
114
+ When `attempts >= 3` without convergence: this is a persistent trouble spot.
115
+ The loop should escalate rather than keep burning budget.
116
+
117
+ ## Integration Points
118
+
119
+ - **`/goal`**: Rework replaces blind retry in the maker-oracle loop
120
+ - **`completion-oracle.js`**: Oracle's `failReasons` feed into blame parsing
121
+ - **`/triage`**: Escalated rework items go to triage inbox
122
+ - **Specialist agents**: Rework briefs route to appropriate specialists
123
+ - **Cost advisor**: Rework attempts count toward goal budget
124
+
125
+ ## Hard Bans
126
+
127
+ - DO NOT retry without blame analysis (blind retry wastes budget)
128
+ - DO NOT modify test files during rework (tests define correct behavior)
129
+ - DO NOT expand scope beyond blamed files (scope creep = new bugs)
130
+ - DO NOT rework indefinitely (3 attempts max per location)
131
+ - DO NOT skip tracking (rework history enables pattern detection)