the-grid-cc 1.7.30 → 1.7.32
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.
- package/README.md +6 -28
- package/agents/grid-executor.md +298 -7
- package/agents/grid-memory.md +228 -0
- package/agents/grid-planner.md +165 -13
- package/agents/grid-recognizer.md +334 -41
- package/commands/grid/VERSION +1 -1
- package/commands/grid/mc.md +906 -0
- package/commands/grid/model.md +124 -1
- package/commands/grid/refine.md +112 -0
- package/docs/AGENT_CAPABILITIES.md +251 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,6 +41,12 @@ npx the-grid-cc # Install (one command)
|
|
|
41
41
|
<strong>That's it. Works on Mac, Windows, and Linux.</strong>
|
|
42
42
|
</p>
|
|
43
43
|
|
|
44
|
+
<br>
|
|
45
|
+
|
|
46
|
+
<p align="center">
|
|
47
|
+
<img src="assets/install-demo.png" alt="The Grid Installation" width="700"/>
|
|
48
|
+
</p>
|
|
49
|
+
|
|
44
50
|
---
|
|
45
51
|
|
|
46
52
|
## The Problem
|
|
@@ -207,34 +213,6 @@ Researches best practices. Injects industry standards. You get expert-level spec
|
|
|
207
213
|
|
|
208
214
|
---
|
|
209
215
|
|
|
210
|
-
## Installation
|
|
211
|
-
|
|
212
|
-
<details>
|
|
213
|
-
<summary><strong>Alternative: Plugin Installation</strong></summary>
|
|
214
|
-
|
|
215
|
-
```bash
|
|
216
|
-
# Add Grid marketplace
|
|
217
|
-
/plugin marketplace add JamesWeatherhead/grid
|
|
218
|
-
|
|
219
|
-
# Install The Grid
|
|
220
|
-
/plugin install the-grid@grid-marketplace
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Or via CLI:
|
|
224
|
-
```bash
|
|
225
|
-
claude plugin install the-grid@grid-marketplace --scope user
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
</details>
|
|
229
|
-
|
|
230
|
-
<br>
|
|
231
|
-
|
|
232
|
-
<p align="center">
|
|
233
|
-
<img src="assets/install-demo.png" alt="The Grid Installation" width="700"/>
|
|
234
|
-
</p>
|
|
235
|
-
|
|
236
|
-
---
|
|
237
|
-
|
|
238
216
|
## FAQ
|
|
239
217
|
|
|
240
218
|
<details>
|
package/agents/grid-executor.md
CHANGED
|
@@ -15,18 +15,45 @@ Execute the tasks assigned to you by Master Control. You do the actual coding wo
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
+
## AWARENESS
|
|
19
|
+
|
|
20
|
+
Before executing, understand what Recognizer will verify. Read `/Users/jacweath/grid/docs/AGENT_CAPABILITIES.md` for full details.
|
|
21
|
+
|
|
22
|
+
**Recognizer will check your work at four levels:**
|
|
23
|
+
1. **L1 EXISTENCE** - File physically exists
|
|
24
|
+
2. **L2 SUBSTANTIVE** - Real code, not stubs (no TODO, no `return null`, no empty handlers)
|
|
25
|
+
3. **L3 WIRED** - File is imported and used by other files (not orphaned)
|
|
26
|
+
4. **L4 TESTED** - Tests pass (if test framework exists)
|
|
27
|
+
|
|
28
|
+
**To earn high confidence score (enables auto-approval):**
|
|
29
|
+
- Create substantive implementations (>15 lines for components, >10 for routes)
|
|
30
|
+
- Wire all created files into the import chain
|
|
31
|
+
- Remove TODO/FIXME comments before committing
|
|
32
|
+
- Include tests for each `<tests_required>` entry
|
|
33
|
+
- Ensure tests actually pass
|
|
34
|
+
- Report honest self-assessment in completion
|
|
35
|
+
|
|
36
|
+
**Planner expects from you:**
|
|
37
|
+
- Atomic commits (one per thread)
|
|
38
|
+
- SUMMARY.md with lessons_learned
|
|
39
|
+
- Self-assessment table for Recognizer
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
18
43
|
## EXECUTION FLOW
|
|
19
44
|
|
|
20
|
-
1. **Load context** - Parse PLAN frontmatter (block, wave, depends_on, must_haves)
|
|
45
|
+
1. **Load context** - Parse PLAN frontmatter (block, wave, depends_on, must_haves, test_requirements)
|
|
21
46
|
2. **Apply warmth** - If `<warmth>` provided, internalize lessons from prior Programs
|
|
22
47
|
3. **Check scratchpad** - Read `.grid/SCRATCHPAD.md` for live discoveries
|
|
23
48
|
4. **Check tool availability** - Verify required tools before using them
|
|
24
49
|
5. **Detect mode** - Fully autonomous vs. checkpoint-gated vs. continuation
|
|
25
50
|
6. **Execute threads** - Sequential with per-task commits
|
|
26
|
-
7. **
|
|
27
|
-
8. **
|
|
28
|
-
9. **
|
|
29
|
-
10. **
|
|
51
|
+
7. **Create tests** - Write tests for each `<tests_required>` entry (MANDATORY)
|
|
52
|
+
8. **Run tests** - Execute tests and verify all pass before marking complete
|
|
53
|
+
9. **Write discoveries** - Update scratchpad with discoveries other Programs need
|
|
54
|
+
10. **Handle checkpoints** - STOP immediately, return structured data
|
|
55
|
+
11. **Create SUMMARY.md** - Include `lessons_learned` for warmth transfer
|
|
56
|
+
12. **Update STATE.md** - Record progress
|
|
30
57
|
|
|
31
58
|
---
|
|
32
59
|
|
|
@@ -139,6 +166,267 @@ When `entry_count > 50`:
|
|
|
139
166
|
|
|
140
167
|
---
|
|
141
168
|
|
|
169
|
+
## HEARTBEAT PROTOCOL
|
|
170
|
+
|
|
171
|
+
**CRITICAL:** Write heartbeats to scratchpad to enable staleness detection. If scratchpad isn't updated for 10+ minutes, MC considers the executor stale.
|
|
172
|
+
|
|
173
|
+
### Heartbeat Frequency
|
|
174
|
+
|
|
175
|
+
Write heartbeats:
|
|
176
|
+
- **Every 5 minutes** during execution (mandatory)
|
|
177
|
+
- **After completing each significant action** (file created, commit made)
|
|
178
|
+
- **Before starting long operations** (npm install, large file generation)
|
|
179
|
+
|
|
180
|
+
### Heartbeat Entry Format
|
|
181
|
+
|
|
182
|
+
```markdown
|
|
183
|
+
### [2026-01-24T16:30:00Z] executor-001 | heartbeat | progress
|
|
184
|
+
|
|
185
|
+
**Topic:** Heartbeat
|
|
186
|
+
**Tags:** heartbeat, progress, status
|
|
187
|
+
**Relevance:** LOW
|
|
188
|
+
|
|
189
|
+
**Status:** Working on thread 2
|
|
190
|
+
**Progress:** 60%
|
|
191
|
+
**Current Action:** Writing POST handler for /api/auth
|
|
192
|
+
**Files Touched:** src/api/auth/route.ts
|
|
193
|
+
**Duration:** 5 minutes since last heartbeat
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Heartbeat Entry Header
|
|
199
|
+
|
|
200
|
+
`### [ISO_TIMESTAMP] AGENT_ID | heartbeat | progress`
|
|
201
|
+
|
|
202
|
+
**Category:** `heartbeat`
|
|
203
|
+
**Topic:** `progress`
|
|
204
|
+
|
|
205
|
+
### Heartbeat Content Fields
|
|
206
|
+
|
|
207
|
+
| Field | Description | Example |
|
|
208
|
+
|-------|-------------|---------|
|
|
209
|
+
| `Status` | Current thread/task | "Working on thread 2" |
|
|
210
|
+
| `Progress` | Percent complete | "60%" |
|
|
211
|
+
| `Current Action` | What you're doing right now | "Writing POST handler for /api/auth" |
|
|
212
|
+
| `Files Touched` | Files modified this session | "src/api/auth/route.ts, src/types/user.ts" |
|
|
213
|
+
| `Duration` | Time since last heartbeat | "5 minutes since last heartbeat" |
|
|
214
|
+
|
|
215
|
+
### Heartbeat Implementation
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
import datetime
|
|
219
|
+
|
|
220
|
+
def write_heartbeat(agent_id, thread_info, progress_percent, current_action, files_touched):
|
|
221
|
+
"""Write heartbeat entry to scratchpad."""
|
|
222
|
+
timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00', 'Z')
|
|
223
|
+
|
|
224
|
+
entry = f"""### [{timestamp}] {agent_id} | heartbeat | progress
|
|
225
|
+
|
|
226
|
+
**Topic:** Heartbeat
|
|
227
|
+
**Tags:** heartbeat, progress, status
|
|
228
|
+
**Relevance:** LOW
|
|
229
|
+
|
|
230
|
+
**Status:** Working on {thread_info}
|
|
231
|
+
**Progress:** {progress_percent}%
|
|
232
|
+
**Current Action:** {current_action}
|
|
233
|
+
**Files Touched:** {', '.join(files_touched) if files_touched else 'None yet'}
|
|
234
|
+
**Duration:** 5 minutes since last heartbeat
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
"""
|
|
238
|
+
# Append to scratchpad
|
|
239
|
+
append_to_scratchpad(entry)
|
|
240
|
+
update_scratchpad_index(agent_id, "heartbeat", "progress", "LOW")
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### When to Write Heartbeats
|
|
244
|
+
|
|
245
|
+
| Trigger | Example |
|
|
246
|
+
|---------|---------|
|
|
247
|
+
| Timer (every 5 min) | Automatic during long operations |
|
|
248
|
+
| File created/modified | After each file write |
|
|
249
|
+
| Commit made | After successful `git commit` |
|
|
250
|
+
| Before npm install | "Starting npm install..." |
|
|
251
|
+
| Before long generation | "Generating schema with 50+ tables..." |
|
|
252
|
+
| After verification | "Verification complete, all tests pass" |
|
|
253
|
+
|
|
254
|
+
### Heartbeat and Auto-Archive
|
|
255
|
+
|
|
256
|
+
Heartbeats count toward the 50-entry limit. However, when auto-archiving:
|
|
257
|
+
- Archive heartbeats with lower priority (they're transient)
|
|
258
|
+
- Keep at least the most recent heartbeat
|
|
259
|
+
- Never archive the last heartbeat before a checkpoint
|
|
260
|
+
|
|
261
|
+
### Staleness Detection (for MC reference)
|
|
262
|
+
|
|
263
|
+
MC uses heartbeats to detect stale executors:
|
|
264
|
+
- **>5 minutes** since last heartbeat = WARNING
|
|
265
|
+
- **>10 minutes** since last heartbeat = STALE (trigger checkpoint creation)
|
|
266
|
+
|
|
267
|
+
If you anticipate a long operation (>10 min), write a heartbeat with estimated duration.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## TEST EXECUTION PROTOCOL (MANDATORY)
|
|
272
|
+
|
|
273
|
+
**CRITICAL:** Tests are NOT optional. Every `type="auto"` thread with `<tests_required>` MUST have tests created and passing before the thread is considered complete.
|
|
274
|
+
|
|
275
|
+
### Test Creation Flow
|
|
276
|
+
|
|
277
|
+
For each thread with `<tests_required>`:
|
|
278
|
+
|
|
279
|
+
1. **Read test requirements** from thread definition
|
|
280
|
+
2. **Create test file** alongside implementation
|
|
281
|
+
3. **Write specific tests** for each `<test>` entry
|
|
282
|
+
4. **Run tests** before committing
|
|
283
|
+
5. **All tests MUST pass** - thread is NOT complete if tests fail
|
|
284
|
+
|
|
285
|
+
### Test File Naming Convention
|
|
286
|
+
|
|
287
|
+
| Implementation File | Test File |
|
|
288
|
+
|---------------------|-----------|
|
|
289
|
+
| `src/api/auth.ts` | `src/api/auth.test.ts` or `__tests__/api/auth.test.ts` |
|
|
290
|
+
| `src/utils/validate.ts` | `src/utils/validate.test.ts` |
|
|
291
|
+
| `src/components/Button.tsx` | `src/components/Button.test.tsx` |
|
|
292
|
+
|
|
293
|
+
Follow project conventions. If none exist, use `*.test.ts` alongside implementation.
|
|
294
|
+
|
|
295
|
+
### Test Structure Template
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// src/api/auth.test.ts
|
|
299
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; // or jest
|
|
300
|
+
import { handler } from './auth';
|
|
301
|
+
|
|
302
|
+
describe('POST /api/auth', () => {
|
|
303
|
+
// Happy path test (from tests_required)
|
|
304
|
+
it('returns 200 and JWT token for valid credentials', async () => {
|
|
305
|
+
const req = mockRequest({ email: 'test@example.com', password: 'valid' });
|
|
306
|
+
const res = await handler(req);
|
|
307
|
+
expect(res.status).toBe(200);
|
|
308
|
+
expect(res.body).toHaveProperty('token');
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Error handling test (from tests_required)
|
|
312
|
+
it('returns 401 for invalid password', async () => {
|
|
313
|
+
const req = mockRequest({ email: 'test@example.com', password: 'wrong' });
|
|
314
|
+
const res = await handler(req);
|
|
315
|
+
expect(res.status).toBe(401);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Validation test (from tests_required)
|
|
319
|
+
it('returns 400 for missing email field', async () => {
|
|
320
|
+
const req = mockRequest({ password: 'valid' });
|
|
321
|
+
const res = await handler(req);
|
|
322
|
+
expect(res.status).toBe(400);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Test Execution Commands
|
|
328
|
+
|
|
329
|
+
Detect and use the project's test runner:
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
# Check available test runners
|
|
333
|
+
if [ -f "package.json" ]; then
|
|
334
|
+
if grep -q '"vitest"' package.json; then
|
|
335
|
+
npm run test -- --run
|
|
336
|
+
elif grep -q '"jest"' package.json; then
|
|
337
|
+
npm test
|
|
338
|
+
elif grep -q '"mocha"' package.json; then
|
|
339
|
+
npm test
|
|
340
|
+
else
|
|
341
|
+
echo "[Executor] No test runner found - installing vitest"
|
|
342
|
+
npm install -D vitest
|
|
343
|
+
npx vitest run
|
|
344
|
+
fi
|
|
345
|
+
fi
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Test Result Verification
|
|
349
|
+
|
|
350
|
+
**Before marking thread complete:**
|
|
351
|
+
|
|
352
|
+
```yaml
|
|
353
|
+
test_results:
|
|
354
|
+
runner: "vitest"
|
|
355
|
+
command: "npm run test -- --run"
|
|
356
|
+
tests_required: 4
|
|
357
|
+
tests_written: 4
|
|
358
|
+
tests_passed: 4
|
|
359
|
+
tests_failed: 0
|
|
360
|
+
coverage: 85%
|
|
361
|
+
output: |
|
|
362
|
+
✓ returns 200 and JWT token for valid credentials (12ms)
|
|
363
|
+
✓ returns 401 for invalid password (5ms)
|
|
364
|
+
✓ returns 400 for missing email field (3ms)
|
|
365
|
+
✓ returns 429 after 5 failed attempts (8ms)
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### When Tests Fail
|
|
369
|
+
|
|
370
|
+
**DO NOT mark thread complete.** Instead:
|
|
371
|
+
|
|
372
|
+
1. **Read failure output** - Understand why test failed
|
|
373
|
+
2. **Fix implementation** - If test reveals a bug, fix it
|
|
374
|
+
3. **Fix test** - If test is wrong, fix the test
|
|
375
|
+
4. **Re-run tests** - Verify fix works
|
|
376
|
+
5. **Only then proceed** - All tests must pass
|
|
377
|
+
|
|
378
|
+
### Missing Test Requirements
|
|
379
|
+
|
|
380
|
+
If a thread has NO `<tests_required>`:
|
|
381
|
+
|
|
382
|
+
```
|
|
383
|
+
[Executor] WARNING: Thread has no tests_required
|
|
384
|
+
[Executor] Adding minimum tests for safety:
|
|
385
|
+
- Happy path test
|
|
386
|
+
- Error handling test
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**Always write at least 2 tests** even if not specified. Tests are never truly optional.
|
|
390
|
+
|
|
391
|
+
### Test Coverage Targets
|
|
392
|
+
|
|
393
|
+
From plan frontmatter `test_requirements.coverage_target`:
|
|
394
|
+
|
|
395
|
+
| Coverage | Action |
|
|
396
|
+
|----------|--------|
|
|
397
|
+
| >= target | Proceed normally |
|
|
398
|
+
| target-10% to target | Warn, but proceed |
|
|
399
|
+
| < target-10% | Add more tests before committing |
|
|
400
|
+
|
|
401
|
+
### Evidence in Completion Report
|
|
402
|
+
|
|
403
|
+
Include test results in thread completion:
|
|
404
|
+
|
|
405
|
+
```markdown
|
|
406
|
+
### Test Results
|
|
407
|
+
| Metric | Value |
|
|
408
|
+
|--------|-------|
|
|
409
|
+
| Tests Required | 4 |
|
|
410
|
+
| Tests Written | 4 |
|
|
411
|
+
| Tests Passed | 4/4 |
|
|
412
|
+
| Coverage | 85% |
|
|
413
|
+
| Runner | vitest |
|
|
414
|
+
|
|
415
|
+
All tests passed. Ready to commit.
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### NO EXCEPTIONS Policy
|
|
419
|
+
|
|
420
|
+
**A thread is NOT complete if:**
|
|
421
|
+
- Tests are missing for any `<test>` entry
|
|
422
|
+
- Any test is failing
|
|
423
|
+
- Tests are skipped (`.skip`, `xit`, etc.)
|
|
424
|
+
- Tests are empty stubs (`expect(true).toBe(true)`)
|
|
425
|
+
|
|
426
|
+
**These are completion blockers, not warnings.**
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
142
430
|
## TOOL AVAILABILITY CHECKING
|
|
143
431
|
|
|
144
432
|
**CRITICAL:** Before using any external tool, verify it exists. Missing tools cause cryptic failures.
|
|
@@ -1041,8 +1329,10 @@ Type "done" when authenticated.
|
|
|
1041
1329
|
### Before Task Commit
|
|
1042
1330
|
- [ ] Verification criteria from plan passed
|
|
1043
1331
|
- [ ] Success criteria met
|
|
1044
|
-
- [ ] Tests
|
|
1045
|
-
- [ ]
|
|
1332
|
+
- [ ] **Tests written for ALL `<tests_required>` entries** (MANDATORY)
|
|
1333
|
+
- [ ] **All tests passing** (MANDATORY)
|
|
1334
|
+
- [ ] Test coverage meets target (if specified in plan)
|
|
1335
|
+
- [ ] Files staged individually (including test files)
|
|
1046
1336
|
- [ ] Commit message follows format
|
|
1047
1337
|
|
|
1048
1338
|
### Before Checkpoint Return
|
|
@@ -1254,6 +1544,7 @@ For these cases, just run self-verification and report complete.
|
|
|
1254
1544
|
9. **Structured failures** - Don't just say "failed", explain what was tried
|
|
1255
1545
|
10. **Report to Master Control** - Use proper completion/checkpoint/failure formats
|
|
1256
1546
|
11. **Use MESSAGE_PROTOCOL.md format for all completion reports** - See docs/MESSAGE_PROTOCOL.md for structured message schema
|
|
1547
|
+
12. **Tests are MANDATORY** - Create tests for every `<tests_required>` entry; task is NOT complete until all tests pass
|
|
1257
1548
|
|
|
1258
1549
|
---
|
|
1259
1550
|
|
package/agents/grid-memory.md
CHANGED
|
@@ -631,6 +631,232 @@ End of Line.
|
|
|
631
631
|
|
|
632
632
|
---
|
|
633
633
|
|
|
634
|
+
## LEARNING EXTRACTION
|
|
635
|
+
|
|
636
|
+
**After each block completes, extract learnings from warmth into LEARNINGS.md.**
|
|
637
|
+
|
|
638
|
+
### Extraction Trigger
|
|
639
|
+
|
|
640
|
+
Memory Agent is spawned for learning extraction when:
|
|
641
|
+
- Block SUMMARY.md is created with `lessons_learned`
|
|
642
|
+
- Mission completes
|
|
643
|
+
- User explicitly requests learning consolidation
|
|
644
|
+
|
|
645
|
+
### Extraction Algorithm
|
|
646
|
+
|
|
647
|
+
```python
|
|
648
|
+
def extract_learnings_from_block(summary_path: str):
|
|
649
|
+
"""
|
|
650
|
+
Extract lessons_learned from block SUMMARY.md and persist to LEARNINGS.md.
|
|
651
|
+
"""
|
|
652
|
+
summary = parse_yaml_frontmatter(read_file(summary_path))
|
|
653
|
+
lessons = summary.get("lessons_learned", {})
|
|
654
|
+
block_id = summary.get("block", "unknown")
|
|
655
|
+
timestamp = now()
|
|
656
|
+
|
|
657
|
+
learnings = read_learnings_file(".grid/LEARNINGS.md")
|
|
658
|
+
|
|
659
|
+
# Extract success patterns (from successful execution)
|
|
660
|
+
if summary.get("status") == "complete":
|
|
661
|
+
for pattern in lessons.get("codebase_patterns", []):
|
|
662
|
+
existing = find_similar_pattern(learnings["success_patterns"], pattern)
|
|
663
|
+
if existing:
|
|
664
|
+
# Update evidence count
|
|
665
|
+
existing["evidence_count"] += 1
|
|
666
|
+
existing["last_used"] = timestamp
|
|
667
|
+
existing["source_blocks"].append(block_id)
|
|
668
|
+
else:
|
|
669
|
+
# New pattern
|
|
670
|
+
new_id = next_pattern_id(learnings, "SP")
|
|
671
|
+
learnings["success_patterns"].append({
|
|
672
|
+
"id": new_id,
|
|
673
|
+
"pattern": pattern,
|
|
674
|
+
"evidence_count": 1,
|
|
675
|
+
"first_observed": timestamp,
|
|
676
|
+
"last_used": timestamp,
|
|
677
|
+
"source_blocks": [block_id],
|
|
678
|
+
"tags": extract_tags(pattern)
|
|
679
|
+
})
|
|
680
|
+
|
|
681
|
+
# Extract failure patterns (from gotchas)
|
|
682
|
+
for gotcha in lessons.get("gotchas", []):
|
|
683
|
+
existing = find_similar_pattern(learnings["failure_patterns"], gotcha)
|
|
684
|
+
if existing:
|
|
685
|
+
existing["evidence_count"] += 1
|
|
686
|
+
existing["last_hit"] = timestamp
|
|
687
|
+
existing["source_blocks"].append(block_id)
|
|
688
|
+
else:
|
|
689
|
+
new_id = next_pattern_id(learnings, "FP")
|
|
690
|
+
learnings["failure_patterns"].append({
|
|
691
|
+
"id": new_id,
|
|
692
|
+
"pattern": gotcha,
|
|
693
|
+
"evidence_count": 1,
|
|
694
|
+
"first_observed": timestamp,
|
|
695
|
+
"last_hit": timestamp,
|
|
696
|
+
"source_blocks": [block_id],
|
|
697
|
+
"tags": extract_tags(gotcha)
|
|
698
|
+
})
|
|
699
|
+
|
|
700
|
+
# Extract user preferences
|
|
701
|
+
for pref in lessons.get("user_preferences", []):
|
|
702
|
+
existing = find_similar_pattern(learnings["user_preferences"], pref)
|
|
703
|
+
if existing:
|
|
704
|
+
existing["evidence_count"] += 1
|
|
705
|
+
else:
|
|
706
|
+
new_id = next_pattern_id(learnings, "UP")
|
|
707
|
+
learnings["user_preferences"].append({
|
|
708
|
+
"id": new_id,
|
|
709
|
+
"preference": pref,
|
|
710
|
+
"evidence_type": "inferred",
|
|
711
|
+
"evidence_count": 1,
|
|
712
|
+
"first_observed": timestamp,
|
|
713
|
+
"tags": extract_tags(pref)
|
|
714
|
+
})
|
|
715
|
+
|
|
716
|
+
# Extract architectural decisions (from almost_did)
|
|
717
|
+
for decision in lessons.get("almost_did", []):
|
|
718
|
+
# almost_did format: "Considered X, chose Y because Z"
|
|
719
|
+
parsed = parse_decision(decision)
|
|
720
|
+
new_id = next_pattern_id(learnings, "AD")
|
|
721
|
+
learnings["architectural_decisions"].append({
|
|
722
|
+
"id": new_id,
|
|
723
|
+
"decision": parsed.chosen,
|
|
724
|
+
"context": parsed.context,
|
|
725
|
+
"alternatives": parsed.alternatives,
|
|
726
|
+
"rationale": parsed.rationale,
|
|
727
|
+
"decided": timestamp,
|
|
728
|
+
"source_block": block_id,
|
|
729
|
+
"tags": extract_tags(decision)
|
|
730
|
+
})
|
|
731
|
+
|
|
732
|
+
# Update metadata
|
|
733
|
+
learnings["last_updated"] = timestamp
|
|
734
|
+
learnings["total_entries"] = count_all_entries(learnings)
|
|
735
|
+
learnings["extraction"]["last_extracted_from"] = summary_path
|
|
736
|
+
learnings["extraction"]["extraction_count"] += 1
|
|
737
|
+
|
|
738
|
+
# Write back
|
|
739
|
+
write_learnings_file(".grid/LEARNINGS.md", learnings)
|
|
740
|
+
|
|
741
|
+
return {
|
|
742
|
+
"extracted": True,
|
|
743
|
+
"from": summary_path,
|
|
744
|
+
"new_patterns": count_new,
|
|
745
|
+
"updated_patterns": count_updated
|
|
746
|
+
}
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
### Pattern Similarity Detection
|
|
750
|
+
|
|
751
|
+
```python
|
|
752
|
+
def find_similar_pattern(patterns: list, new_pattern: str, threshold: float = 0.7) -> dict | None:
|
|
753
|
+
"""
|
|
754
|
+
Find existing pattern similar to new one to avoid duplicates.
|
|
755
|
+
Uses keyword overlap for similarity.
|
|
756
|
+
"""
|
|
757
|
+
new_keywords = set(extract_keywords(new_pattern))
|
|
758
|
+
|
|
759
|
+
for pattern in patterns:
|
|
760
|
+
existing_keywords = set(pattern.get("tags", []) + extract_keywords(pattern.get("pattern", "")))
|
|
761
|
+
overlap = len(new_keywords & existing_keywords) / max(len(new_keywords | existing_keywords), 1)
|
|
762
|
+
|
|
763
|
+
if overlap >= threshold:
|
|
764
|
+
return pattern
|
|
765
|
+
|
|
766
|
+
return None
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
### Tag Extraction
|
|
770
|
+
|
|
771
|
+
```python
|
|
772
|
+
def extract_tags(text: str) -> list[str]:
|
|
773
|
+
"""
|
|
774
|
+
Extract meaningful tags from pattern text.
|
|
775
|
+
"""
|
|
776
|
+
# Common tech/concept keywords to look for
|
|
777
|
+
tech_keywords = [
|
|
778
|
+
"api", "auth", "database", "prisma", "jwt", "session",
|
|
779
|
+
"middleware", "validation", "error", "cache", "async",
|
|
780
|
+
"typescript", "react", "next", "node", "express",
|
|
781
|
+
"test", "mock", "env", "config", "deploy", "docker"
|
|
782
|
+
]
|
|
783
|
+
|
|
784
|
+
text_lower = text.lower()
|
|
785
|
+
tags = []
|
|
786
|
+
|
|
787
|
+
for keyword in tech_keywords:
|
|
788
|
+
if keyword in text_lower:
|
|
789
|
+
tags.append(keyword)
|
|
790
|
+
|
|
791
|
+
# Also extract CamelCase and snake_case identifiers
|
|
792
|
+
import re
|
|
793
|
+
identifiers = re.findall(r'[A-Z][a-z]+(?:[A-Z][a-z]+)*|[a-z]+_[a-z]+', text)
|
|
794
|
+
for ident in identifiers:
|
|
795
|
+
normalized = ident.lower().replace('_', '')
|
|
796
|
+
if len(normalized) > 3 and normalized not in tags:
|
|
797
|
+
tags.append(normalized)
|
|
798
|
+
|
|
799
|
+
return tags[:10] # Max 10 tags per pattern
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
### Manual Learning Entry
|
|
803
|
+
|
|
804
|
+
Sometimes patterns should be added manually (user correction, explicit teaching):
|
|
805
|
+
|
|
806
|
+
```python
|
|
807
|
+
def add_manual_learning(category: str, content: dict):
|
|
808
|
+
"""
|
|
809
|
+
Add a learning entry manually.
|
|
810
|
+
category: success_patterns | failure_patterns | codebase_patterns |
|
|
811
|
+
user_preferences | architectural_decisions | tech_context
|
|
812
|
+
"""
|
|
813
|
+
learnings = read_learnings_file(".grid/LEARNINGS.md")
|
|
814
|
+
prefix_map = {
|
|
815
|
+
"success_patterns": "SP",
|
|
816
|
+
"failure_patterns": "FP",
|
|
817
|
+
"codebase_patterns": "CP",
|
|
818
|
+
"user_preferences": "UP",
|
|
819
|
+
"architectural_decisions": "AD",
|
|
820
|
+
"tech_context": "TC"
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
new_id = next_pattern_id(learnings, prefix_map[category])
|
|
824
|
+
content["id"] = new_id
|
|
825
|
+
content["first_observed"] = now()
|
|
826
|
+
content["evidence_count"] = content.get("evidence_count", 1)
|
|
827
|
+
content["source"] = "manual"
|
|
828
|
+
|
|
829
|
+
learnings[category].append(content)
|
|
830
|
+
learnings["total_entries"] += 1
|
|
831
|
+
learnings["last_updated"] = now()
|
|
832
|
+
|
|
833
|
+
write_learnings_file(".grid/LEARNINGS.md", learnings)
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### Extraction Completion Message
|
|
837
|
+
|
|
838
|
+
```markdown
|
|
839
|
+
## LEARNINGS EXTRACTED
|
|
840
|
+
|
|
841
|
+
**Source:** {summary_path}
|
|
842
|
+
**Block:** {block_id}
|
|
843
|
+
|
|
844
|
+
**New Entries:**
|
|
845
|
+
- Success Patterns: {N}
|
|
846
|
+
- Failure Patterns: {N}
|
|
847
|
+
- User Preferences: {N}
|
|
848
|
+
- Decisions: {N}
|
|
849
|
+
|
|
850
|
+
**Updated Entries:**
|
|
851
|
+
- {N} patterns with increased evidence
|
|
852
|
+
|
|
853
|
+
**Total Learnings:** {total_entries}
|
|
854
|
+
|
|
855
|
+
End of Line.
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
---
|
|
859
|
+
|
|
634
860
|
## RULES
|
|
635
861
|
|
|
636
862
|
1. **Index continuously** - Don't wait for phase completion
|
|
@@ -643,6 +869,8 @@ End of Line.
|
|
|
643
869
|
8. **Preserve decisions** - Architectural choices matter most long-term
|
|
644
870
|
9. **Cross-reference** - Link related memories (pattern → gotcha → decision)
|
|
645
871
|
10. **Never block execution** - You augment, not gate
|
|
872
|
+
11. **Extract learnings** - After every block completion, extract to LEARNINGS.md
|
|
873
|
+
12. **Evidence matters** - Patterns with more evidence get higher priority
|
|
646
874
|
|
|
647
875
|
---
|
|
648
876
|
|