anvil-dev-framework 0.1.6

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.
Files changed (190) hide show
  1. package/README.md +719 -0
  2. package/VERSION +1 -0
  3. package/docs/ANVIL-REPO-IMPLEMENTATION-PLAN.md +441 -0
  4. package/docs/FIRST-SKILL-TUTORIAL.md +408 -0
  5. package/docs/INSTALLATION-RETRO-NOTES.md +458 -0
  6. package/docs/INSTALLATION.md +984 -0
  7. package/docs/anvil-hud.md +469 -0
  8. package/docs/anvil-init.md +255 -0
  9. package/docs/anvil-state.md +210 -0
  10. package/docs/boris-cherny-ralph-wiggum-insights.md +608 -0
  11. package/docs/command-reference.md +2022 -0
  12. package/docs/hooks-tts.md +368 -0
  13. package/docs/implementation-guide.md +810 -0
  14. package/docs/linear-github-integration.md +247 -0
  15. package/docs/local-issues.md +677 -0
  16. package/docs/patterns/README.md +419 -0
  17. package/docs/planning-responsibilities.md +139 -0
  18. package/docs/session-workflow.md +573 -0
  19. package/docs/simplification-plan-template.md +297 -0
  20. package/docs/simplification-principles.md +129 -0
  21. package/docs/specifications/CCS-RALPH-INTEGRATION-DESIGN.md +633 -0
  22. package/docs/specifications/CCS-RESEARCH-REPORT.md +169 -0
  23. package/docs/specifications/PLAN-ANV-verification-ralph-wiggum.md +403 -0
  24. package/docs/specifications/PLAN-parallel-tracks-anvil-memory-ccs.md +494 -0
  25. package/docs/specifications/SPEC-ANV-VRW/component-01-verify.md +208 -0
  26. package/docs/specifications/SPEC-ANV-VRW/component-02-stop-gate.md +226 -0
  27. package/docs/specifications/SPEC-ANV-VRW/component-03-posttooluse.md +209 -0
  28. package/docs/specifications/SPEC-ANV-VRW/component-04-ralph-wiggum.md +604 -0
  29. package/docs/specifications/SPEC-ANV-VRW/component-05-atomic-actions.md +311 -0
  30. package/docs/specifications/SPEC-ANV-VRW/component-06-verify-subagent.md +264 -0
  31. package/docs/specifications/SPEC-ANV-VRW/component-07-claude-md.md +363 -0
  32. package/docs/specifications/SPEC-ANV-VRW/index.md +182 -0
  33. package/docs/specifications/SPEC-ANV-anvil-memory.md +573 -0
  34. package/docs/specifications/SPEC-ANV-context-checkpoints.md +781 -0
  35. package/docs/specifications/SPEC-ANV-verification-ralph-wiggum.md +789 -0
  36. package/docs/sync.md +122 -0
  37. package/global/CLAUDE.md +140 -0
  38. package/global/agents/verify-app.md +164 -0
  39. package/global/commands/anvil-settings.md +527 -0
  40. package/global/commands/anvil-sync.md +121 -0
  41. package/global/commands/change.md +197 -0
  42. package/global/commands/clarify.md +252 -0
  43. package/global/commands/cleanup.md +292 -0
  44. package/global/commands/commit-push-pr.md +207 -0
  45. package/global/commands/decay-review.md +127 -0
  46. package/global/commands/discover.md +158 -0
  47. package/global/commands/doc-coverage.md +122 -0
  48. package/global/commands/evidence.md +307 -0
  49. package/global/commands/explore.md +121 -0
  50. package/global/commands/force-exit.md +135 -0
  51. package/global/commands/handoff.md +191 -0
  52. package/global/commands/healthcheck.md +302 -0
  53. package/global/commands/hud.md +84 -0
  54. package/global/commands/insights.md +319 -0
  55. package/global/commands/linear-setup.md +184 -0
  56. package/global/commands/lint-fix.md +198 -0
  57. package/global/commands/orient.md +510 -0
  58. package/global/commands/plan.md +228 -0
  59. package/global/commands/ralph.md +346 -0
  60. package/global/commands/ready.md +182 -0
  61. package/global/commands/release.md +305 -0
  62. package/global/commands/retro.md +96 -0
  63. package/global/commands/shard.md +166 -0
  64. package/global/commands/spec.md +227 -0
  65. package/global/commands/sprint.md +184 -0
  66. package/global/commands/tasks.md +228 -0
  67. package/global/commands/test-and-commit.md +151 -0
  68. package/global/commands/validate.md +132 -0
  69. package/global/commands/verify.md +251 -0
  70. package/global/commands/weekly-review.md +156 -0
  71. package/global/hooks/__pycache__/ralph_context_monitor.cpython-314.pyc +0 -0
  72. package/global/hooks/__pycache__/statusline_agent_sync.cpython-314.pyc +0 -0
  73. package/global/hooks/anvil_memory_observe.ts +322 -0
  74. package/global/hooks/anvil_memory_session.ts +166 -0
  75. package/global/hooks/anvil_memory_stop.ts +187 -0
  76. package/global/hooks/parse_transcript.py +116 -0
  77. package/global/hooks/post_merge_cleanup.sh +132 -0
  78. package/global/hooks/post_tool_format.sh +215 -0
  79. package/global/hooks/ralph_context_monitor.py +240 -0
  80. package/global/hooks/ralph_stop.sh +502 -0
  81. package/global/hooks/statusline.sh +1110 -0
  82. package/global/hooks/statusline_agent_sync.py +224 -0
  83. package/global/hooks/stop_gate.sh +250 -0
  84. package/global/lib/.claude/anvil-state.json +21 -0
  85. package/global/lib/__pycache__/agent_registry.cpython-314.pyc +0 -0
  86. package/global/lib/__pycache__/claim_service.cpython-314.pyc +0 -0
  87. package/global/lib/__pycache__/coderabbit_service.cpython-314.pyc +0 -0
  88. package/global/lib/__pycache__/config_service.cpython-314.pyc +0 -0
  89. package/global/lib/__pycache__/coordination_service.cpython-314.pyc +0 -0
  90. package/global/lib/__pycache__/doc_coverage_service.cpython-314.pyc +0 -0
  91. package/global/lib/__pycache__/gate_logger.cpython-314.pyc +0 -0
  92. package/global/lib/__pycache__/github_service.cpython-314.pyc +0 -0
  93. package/global/lib/__pycache__/hygiene_service.cpython-314.pyc +0 -0
  94. package/global/lib/__pycache__/issue_models.cpython-314.pyc +0 -0
  95. package/global/lib/__pycache__/issue_provider.cpython-314.pyc +0 -0
  96. package/global/lib/__pycache__/linear_data_service.cpython-314.pyc +0 -0
  97. package/global/lib/__pycache__/linear_provider.cpython-314.pyc +0 -0
  98. package/global/lib/__pycache__/local_provider.cpython-314.pyc +0 -0
  99. package/global/lib/__pycache__/quality_service.cpython-314.pyc +0 -0
  100. package/global/lib/__pycache__/ralph_state.cpython-314.pyc +0 -0
  101. package/global/lib/__pycache__/state_manager.cpython-314.pyc +0 -0
  102. package/global/lib/__pycache__/transcript_parser.cpython-314.pyc +0 -0
  103. package/global/lib/__pycache__/verification_runner.cpython-314.pyc +0 -0
  104. package/global/lib/__pycache__/verify_iteration.cpython-314.pyc +0 -0
  105. package/global/lib/__pycache__/verify_subagent.cpython-314.pyc +0 -0
  106. package/global/lib/agent_registry.py +995 -0
  107. package/global/lib/anvil-state.sh +435 -0
  108. package/global/lib/claim_service.py +515 -0
  109. package/global/lib/coderabbit_service.py +314 -0
  110. package/global/lib/config_service.py +423 -0
  111. package/global/lib/coordination_service.py +331 -0
  112. package/global/lib/doc_coverage_service.py +1305 -0
  113. package/global/lib/gate_logger.py +316 -0
  114. package/global/lib/github_service.py +310 -0
  115. package/global/lib/handoff_generator.py +775 -0
  116. package/global/lib/hygiene_service.py +712 -0
  117. package/global/lib/issue_models.py +257 -0
  118. package/global/lib/issue_provider.py +339 -0
  119. package/global/lib/linear_data_service.py +210 -0
  120. package/global/lib/linear_provider.py +987 -0
  121. package/global/lib/linear_provider.py.backup +671 -0
  122. package/global/lib/local_provider.py +486 -0
  123. package/global/lib/orient_fast.py +457 -0
  124. package/global/lib/quality_service.py +470 -0
  125. package/global/lib/ralph_prompt_generator.py +563 -0
  126. package/global/lib/ralph_state.py +1202 -0
  127. package/global/lib/state_manager.py +417 -0
  128. package/global/lib/transcript_parser.py +597 -0
  129. package/global/lib/verification_runner.py +557 -0
  130. package/global/lib/verify_iteration.py +490 -0
  131. package/global/lib/verify_subagent.py +250 -0
  132. package/global/skills/README.md +155 -0
  133. package/global/skills/quality-gates/SKILL.md +252 -0
  134. package/global/skills/skill-template/SKILL.md +109 -0
  135. package/global/skills/testing-strategies/SKILL.md +337 -0
  136. package/global/templates/CHANGE-template.md +105 -0
  137. package/global/templates/HANDOFF-template.md +63 -0
  138. package/global/templates/PLAN-template.md +111 -0
  139. package/global/templates/SPEC-template.md +93 -0
  140. package/global/templates/ralph/PROMPT.md.template +89 -0
  141. package/global/templates/ralph/fix_plan.md.template +31 -0
  142. package/global/templates/ralph/progress.txt.template +23 -0
  143. package/global/tests/__pycache__/test_doc_coverage.cpython-314.pyc +0 -0
  144. package/global/tests/test_doc_coverage.py +520 -0
  145. package/global/tests/test_issue_models.py +299 -0
  146. package/global/tests/test_local_provider.py +323 -0
  147. package/global/tools/README.md +178 -0
  148. package/global/tools/__pycache__/anvil-hud.cpython-314.pyc +0 -0
  149. package/global/tools/anvil-hud.py +3622 -0
  150. package/global/tools/anvil-hud.py.bak +3318 -0
  151. package/global/tools/anvil-issue.py +432 -0
  152. package/global/tools/anvil-memory/CLAUDE.md +49 -0
  153. package/global/tools/anvil-memory/README.md +42 -0
  154. package/global/tools/anvil-memory/bun.lock +25 -0
  155. package/global/tools/anvil-memory/bunfig.toml +9 -0
  156. package/global/tools/anvil-memory/package.json +23 -0
  157. package/global/tools/anvil-memory/src/__tests__/ccs/context-monitor.test.ts +535 -0
  158. package/global/tools/anvil-memory/src/__tests__/ccs/edge-cases.test.ts +645 -0
  159. package/global/tools/anvil-memory/src/__tests__/ccs/fixtures.ts +363 -0
  160. package/global/tools/anvil-memory/src/__tests__/ccs/index.ts +8 -0
  161. package/global/tools/anvil-memory/src/__tests__/ccs/integration.test.ts +417 -0
  162. package/global/tools/anvil-memory/src/__tests__/ccs/prompt-generator.test.ts +571 -0
  163. package/global/tools/anvil-memory/src/__tests__/ccs/ralph-stop.test.ts +440 -0
  164. package/global/tools/anvil-memory/src/__tests__/ccs/test-utils.ts +252 -0
  165. package/global/tools/anvil-memory/src/__tests__/commands.test.ts +657 -0
  166. package/global/tools/anvil-memory/src/__tests__/db.test.ts +641 -0
  167. package/global/tools/anvil-memory/src/__tests__/hooks.test.ts +272 -0
  168. package/global/tools/anvil-memory/src/__tests__/performance.test.ts +427 -0
  169. package/global/tools/anvil-memory/src/__tests__/test-utils.ts +113 -0
  170. package/global/tools/anvil-memory/src/commands/checkpoint.ts +197 -0
  171. package/global/tools/anvil-memory/src/commands/get.ts +115 -0
  172. package/global/tools/anvil-memory/src/commands/init.ts +94 -0
  173. package/global/tools/anvil-memory/src/commands/observe.ts +163 -0
  174. package/global/tools/anvil-memory/src/commands/search.ts +112 -0
  175. package/global/tools/anvil-memory/src/db.ts +638 -0
  176. package/global/tools/anvil-memory/src/index.ts +205 -0
  177. package/global/tools/anvil-memory/src/types.ts +122 -0
  178. package/global/tools/anvil-memory/tsconfig.json +29 -0
  179. package/global/tools/ralph-loop.sh +359 -0
  180. package/package.json +45 -0
  181. package/scripts/anvil +822 -0
  182. package/scripts/extract_patterns.py +222 -0
  183. package/scripts/init-project.sh +541 -0
  184. package/scripts/install.sh +229 -0
  185. package/scripts/postinstall.js +41 -0
  186. package/scripts/rollback.sh +188 -0
  187. package/scripts/sync.sh +623 -0
  188. package/scripts/test-statusline.sh +248 -0
  189. package/scripts/update_claude_md.py +224 -0
  190. package/scripts/verify.sh +255 -0
@@ -0,0 +1,417 @@
1
+ /**
2
+ * CCS Integration Tests
3
+ *
4
+ * Tests for the full Context Checkpoint System workflow:
5
+ * Context Monitor → Checkpoint → Ralph Stop → Prompt Generator
6
+ */
7
+
8
+ import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
9
+ import { existsSync, readFileSync } from 'fs';
10
+ import { join } from 'path';
11
+ import {
12
+ TestStateManager,
13
+ runContextMonitor,
14
+ runRalphStop,
15
+ runPythonHook,
16
+ isJqAvailable,
17
+ isPythonAvailable,
18
+ LIB_DIR,
19
+ } from './test-utils';
20
+ import {
21
+ createMinimalRalphState,
22
+ createFullRalphState,
23
+ CONTEXT_INPUTS,
24
+ } from './fixtures';
25
+
26
+ const PROMPT_GENERATOR_PATH = join(LIB_DIR, 'ralph_prompt_generator.py');
27
+
28
+ describe('CCS Integration', () => {
29
+ let stateManager: TestStateManager;
30
+ let jqAvailable: boolean;
31
+ let pythonAvailable: boolean;
32
+
33
+ beforeEach(async () => {
34
+ stateManager = new TestStateManager('ccs-integration');
35
+ stateManager.init();
36
+ [jqAvailable, pythonAvailable] = await Promise.all([
37
+ isJqAvailable(),
38
+ isPythonAvailable(),
39
+ ]);
40
+ });
41
+
42
+ afterEach(() => {
43
+ stateManager.cleanup();
44
+ });
45
+
46
+ describe('Checkpoint Creation Flow', () => {
47
+ test('L2 context triggers checkpoint creation in state', async () => {
48
+ if (!pythonAvailable) {
49
+ console.log('Skipping: Python not available');
50
+ return;
51
+ }
52
+
53
+ // Setup: Ralph state without checkpoint
54
+ const initialState = createFullRalphState({
55
+ context_checkpoint: undefined,
56
+ });
57
+ stateManager.writeState(initialState);
58
+
59
+ // Act: Run context monitor with L2 context
60
+ const result = await runContextMonitor(CONTEXT_INPUTS.L2_MEDIUM, stateManager);
61
+
62
+ // Verify: Monitor detected L2
63
+ expect(result.exitCode).toBe(0);
64
+ expect(result.stdout).toContain('L2');
65
+
66
+ // Verify: State now has checkpoint
67
+ const finalState = stateManager.readState();
68
+ expect(finalState.context_checkpoint).toBeDefined();
69
+ if (finalState.context_checkpoint) {
70
+ const checkpoint = finalState.context_checkpoint as Record<string, unknown>;
71
+ expect(checkpoint.active).toBe(true);
72
+ expect(checkpoint.level).toBe('L2');
73
+ }
74
+ });
75
+
76
+ test('L3 context triggers emergency checkpoint', async () => {
77
+ if (!pythonAvailable) {
78
+ console.log('Skipping: Python not available');
79
+ return;
80
+ }
81
+
82
+ const initialState = createFullRalphState({
83
+ task_name: 'Emergency Test Task',
84
+ todo_items: ['Urgent item'],
85
+ });
86
+ stateManager.writeState(initialState);
87
+
88
+ const result = await runContextMonitor(CONTEXT_INPUTS.L3_MEDIUM, stateManager);
89
+
90
+ // L3 exits with code 1 to signal emergency stop
91
+ expect(result.exitCode).toBe(1);
92
+ expect(result.stdout).toContain('L3');
93
+
94
+ const finalState = stateManager.readState();
95
+ expect(finalState.context_checkpoint).toBeDefined();
96
+ if (finalState.context_checkpoint) {
97
+ const checkpoint = finalState.context_checkpoint as Record<string, unknown>;
98
+ expect(checkpoint.level).toBe('L3');
99
+ }
100
+ });
101
+
102
+ test('handoff file is referenced in checkpoint', async () => {
103
+ if (!pythonAvailable) {
104
+ console.log('Skipping: Python not available');
105
+ return;
106
+ }
107
+
108
+ const initialState = createFullRalphState();
109
+ stateManager.writeState(initialState);
110
+
111
+ await runContextMonitor(CONTEXT_INPUTS.L2_LOW, stateManager);
112
+
113
+ const finalState = stateManager.readState();
114
+ if (finalState.context_checkpoint) {
115
+ const checkpoint = finalState.context_checkpoint as Record<string, unknown>;
116
+ expect(checkpoint.handoff_file).toBeDefined();
117
+ expect(typeof checkpoint.handoff_file).toBe('string');
118
+ expect((checkpoint.handoff_file as string)).toContain('.claude/handoffs/');
119
+ }
120
+ });
121
+ });
122
+
123
+ describe('Ralph Stop Checkpoint Detection', () => {
124
+ test('ralph stop detects active checkpoint and restarts', async () => {
125
+ if (!jqAvailable) {
126
+ console.log('Skipping: jq not available');
127
+ return;
128
+ }
129
+
130
+ // Setup: Create state with active checkpoint
131
+ const stateWithCheckpoint = createFullRalphState({
132
+ context_checkpoint: {
133
+ active: true,
134
+ level: 'L2',
135
+ percent_at_checkpoint: 87,
136
+ timestamp: new Date().toISOString(),
137
+ handoff_file: '.claude/handoffs/test.md',
138
+ resume_summary: 'Test resume',
139
+ files_in_progress: [{ path: 'test.ts', start_line: 1, end_line: 10 }],
140
+ current_todo_item: 'Current task',
141
+ progress_on_item: 'In progress',
142
+ },
143
+ });
144
+ stateManager.writeState(stateWithCheckpoint);
145
+
146
+ // Act: Run ralph stop
147
+ const result = await runRalphStop(stateManager);
148
+
149
+ // Verify: Should restart loop (exit 1) due to checkpoint
150
+ expect(result.exitCode).toBe(1);
151
+ expect(result.stderr).toContain('checkpoint');
152
+ });
153
+
154
+ test('ralph stop continues without checkpoint', async () => {
155
+ if (!jqAvailable) {
156
+ console.log('Skipping: jq not available');
157
+ return;
158
+ }
159
+
160
+ const stateNoCheckpoint = createMinimalRalphState({ iteration: 5 });
161
+ stateManager.writeState(stateNoCheckpoint);
162
+
163
+ const result = await runRalphStop(stateManager);
164
+
165
+ // Should restart loop normally
166
+ expect(result.exitCode).toBe(1);
167
+ });
168
+ });
169
+
170
+ describe('Prompt Generator Integration', () => {
171
+ test('prompt generator reads checkpoint state correctly', async () => {
172
+ if (!pythonAvailable) {
173
+ console.log('Skipping: Python not available');
174
+ return;
175
+ }
176
+
177
+ // Setup: First create checkpoint via context monitor
178
+ const initialState = createFullRalphState({
179
+ task_name: 'Integration Test Task',
180
+ objective: 'Complete the integration',
181
+ todo_items: ['Step 1', 'Step 2', 'Step 3'],
182
+ completed_items: ['Setup'],
183
+ });
184
+ stateManager.writeState(initialState);
185
+
186
+ // Trigger checkpoint
187
+ await runContextMonitor(CONTEXT_INPUTS.L2_HIGH, stateManager);
188
+
189
+ // Now generate prompt from checkpointed state
190
+ const result = await runPythonHook(
191
+ PROMPT_GENERATOR_PATH,
192
+ {},
193
+ {
194
+ cwd: stateManager.baseDir,
195
+ args: ['--state', stateManager.stateFile, '--dry-run'],
196
+ }
197
+ );
198
+
199
+ expect(result.exitCode).toBe(0);
200
+ expect(result.stdout).toContain('Integration Test Task');
201
+ expect(result.stdout).toContain('L2');
202
+ expect(result.stdout).toContain('Step 1');
203
+ });
204
+
205
+ test('prompt generator writes valid PROMPT.md', async () => {
206
+ if (!pythonAvailable) {
207
+ console.log('Skipping: Python not available');
208
+ return;
209
+ }
210
+
211
+ const state = createFullRalphState({
212
+ context_checkpoint: {
213
+ active: true,
214
+ level: 'L2',
215
+ percent_at_checkpoint: 89,
216
+ timestamp: new Date().toISOString(),
217
+ handoff_file: '.claude/handoffs/session.md',
218
+ resume_summary: '',
219
+ files_in_progress: [{ path: 'main.ts', start_line: 100, end_line: 200 }],
220
+ current_todo_item: 'Current work',
221
+ progress_on_item: 'Halfway done',
222
+ },
223
+ });
224
+ stateManager.writeState(state);
225
+
226
+ const promptPath = join(stateManager.baseDir, 'PROMPT.md');
227
+ const result = await runPythonHook(
228
+ PROMPT_GENERATOR_PATH,
229
+ {},
230
+ {
231
+ cwd: stateManager.baseDir,
232
+ args: ['--state', stateManager.stateFile, '--output', promptPath],
233
+ }
234
+ );
235
+
236
+ expect(result.exitCode).toBe(0);
237
+ expect(existsSync(promptPath)).toBe(true);
238
+
239
+ const promptContent = readFileSync(promptPath, 'utf-8');
240
+ expect(promptContent).toContain('Session Resume');
241
+ expect(promptContent).toContain('Resume Instructions');
242
+ expect(promptContent).toContain('main.ts');
243
+ });
244
+ });
245
+
246
+ describe('Full Cycle Test', () => {
247
+ test('complete checkpoint → resume cycle', async () => {
248
+ if (!pythonAvailable || !jqAvailable) {
249
+ console.log('Skipping: Dependencies not available');
250
+ return;
251
+ }
252
+
253
+ // Step 1: Initial state - running task
254
+ const initialState = createFullRalphState({
255
+ iteration: 10,
256
+ task_name: 'Full Cycle Test',
257
+ objective: 'Test complete CCS workflow',
258
+ todo_items: ['Remaining item 1', 'Remaining item 2'],
259
+ completed_items: ['Completed item 1'],
260
+ });
261
+ stateManager.writeState(initialState);
262
+
263
+ // Step 2: Context monitor detects high usage and creates checkpoint
264
+ const monitorResult = await runContextMonitor(CONTEXT_INPUTS.L2_MEDIUM, stateManager);
265
+ expect(monitorResult.exitCode).toBe(0);
266
+
267
+ // Verify checkpoint was created
268
+ const stateAfterMonitor = stateManager.readState();
269
+ expect(stateAfterMonitor.context_checkpoint).toBeDefined();
270
+ const checkpoint = stateAfterMonitor.context_checkpoint as Record<string, unknown>;
271
+ expect(checkpoint.active).toBe(true);
272
+
273
+ // Step 3: Ralph stop detects checkpoint and signals restart
274
+ const stopResult = await runRalphStop(stateManager);
275
+ expect(stopResult.exitCode).toBe(1); // Restart loop
276
+
277
+ // Step 4: Generate resume prompt
278
+ const promptPath = join(stateManager.baseDir, 'PROMPT.md');
279
+ const promptResult = await runPythonHook(
280
+ PROMPT_GENERATOR_PATH,
281
+ {},
282
+ {
283
+ cwd: stateManager.baseDir,
284
+ args: ['--state', stateManager.stateFile, '--output', promptPath],
285
+ }
286
+ );
287
+ expect(promptResult.exitCode).toBe(0);
288
+
289
+ // Step 5: Validate generated prompt
290
+ expect(existsSync(promptPath)).toBe(true);
291
+ const promptContent = readFileSync(promptPath, 'utf-8');
292
+ expect(promptContent).toContain('Full Cycle Test');
293
+ expect(promptContent).toContain('Resume Instructions');
294
+ expect(promptContent).toContain('L2');
295
+ expect(promptContent).toContain('Remaining item 1');
296
+ });
297
+ });
298
+
299
+ describe('Context History Tracking', () => {
300
+ test('context history accumulates across iterations', async () => {
301
+ if (!pythonAvailable) {
302
+ console.log('Skipping: Python not available');
303
+ return;
304
+ }
305
+
306
+ const initialState = createMinimalRalphState({
307
+ iteration: 1,
308
+ context_history: [],
309
+ });
310
+ stateManager.writeState(initialState);
311
+
312
+ // Run several context monitor calls at different levels
313
+ await runContextMonitor(CONTEXT_INPUTS.L0_MEDIUM, stateManager);
314
+
315
+ // Update iteration and run again
316
+ const state1 = stateManager.readState();
317
+ stateManager.writeState({ ...state1, iteration: 2 });
318
+ await runContextMonitor(CONTEXT_INPUTS.L1_LOW, stateManager);
319
+
320
+ // Final check
321
+ const finalState = stateManager.readState();
322
+ const history = finalState.context_history as Array<Record<string, unknown>> | undefined;
323
+
324
+ // History should have entries
325
+ expect(history).toBeDefined();
326
+ if (history) {
327
+ expect(history.length).toBeGreaterThan(0);
328
+ }
329
+ });
330
+
331
+ test('checkpoint event is recorded in history', async () => {
332
+ if (!pythonAvailable) {
333
+ console.log('Skipping: Python not available');
334
+ return;
335
+ }
336
+
337
+ const initialState = createFullRalphState({
338
+ context_history: [
339
+ { iteration: 1, peak_percent: 30, checkpoint: false },
340
+ { iteration: 2, peak_percent: 50, checkpoint: false },
341
+ ],
342
+ });
343
+ stateManager.writeState(initialState);
344
+
345
+ // Trigger checkpoint
346
+ await runContextMonitor(CONTEXT_INPUTS.L2_MEDIUM, stateManager);
347
+
348
+ const finalState = stateManager.readState();
349
+ const history = finalState.context_history as Array<Record<string, unknown>> | undefined;
350
+
351
+ if (history) {
352
+ // Find checkpoint entry
353
+ const checkpointEntry = history.find(h => h.checkpoint === true);
354
+ expect(checkpointEntry).toBeDefined();
355
+ if (checkpointEntry) {
356
+ expect(checkpointEntry.level).toBe('L2');
357
+ }
358
+ }
359
+ });
360
+ });
361
+
362
+ describe('Error Recovery', () => {
363
+ test('system handles missing handoff directory gracefully', async () => {
364
+ if (!pythonAvailable) {
365
+ console.log('Skipping: Python not available');
366
+ return;
367
+ }
368
+
369
+ // Create state but remove handoffs directory
370
+ const state = createFullRalphState();
371
+ stateManager.writeState(state);
372
+
373
+ // This should still work - context monitor should create directory if needed
374
+ const result = await runContextMonitor(CONTEXT_INPUTS.L2_LOW, stateManager);
375
+
376
+ // Should not crash
377
+ expect(typeof result.exitCode).toBe('number');
378
+ });
379
+
380
+ test('system handles sequential state modifications', async () => {
381
+ if (!pythonAvailable) {
382
+ console.log('Skipping: Python not available');
383
+ return;
384
+ }
385
+
386
+ const state = createFullRalphState();
387
+ stateManager.writeState(state);
388
+
389
+ // Run monitors sequentially (concurrent writes are a known race condition)
390
+ const result1 = await runContextMonitor(CONTEXT_INPUTS.L1_LOW, stateManager);
391
+ const result2 = await runContextMonitor(CONTEXT_INPUTS.L1_MEDIUM, stateManager);
392
+
393
+ // Both should complete without crashing
394
+ expect(typeof result1.exitCode).toBe('number');
395
+ expect(typeof result2.exitCode).toBe('number');
396
+
397
+ // State should still be valid JSON
398
+ const finalState = stateManager.readState();
399
+ expect(finalState).toBeDefined();
400
+ expect(finalState.context_history).toBeDefined();
401
+ });
402
+ });
403
+ });
404
+
405
+ describe('CCS Integration - Dependency Check', () => {
406
+ test('all required hook scripts exist', () => {
407
+ const hookPaths = [
408
+ '/Users/alexandercahiz/Projects/anvil-dev-framework/global/hooks/ralph_context_monitor.py',
409
+ '/Users/alexandercahiz/Projects/anvil-dev-framework/global/hooks/ralph_stop.sh',
410
+ '/Users/alexandercahiz/Projects/anvil-dev-framework/global/lib/ralph_prompt_generator.py',
411
+ ];
412
+
413
+ for (const hookPath of hookPaths) {
414
+ expect(existsSync(hookPath)).toBe(true);
415
+ }
416
+ });
417
+ });