cclaw-cli 0.5.16 → 0.6.0
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/dist/artifact-linter.d.ts +13 -0
- package/dist/artifact-linter.js +182 -13
- package/dist/cli.d.ts +4 -2
- package/dist/cli.js +18 -4
- package/dist/config.d.ts +2 -2
- package/dist/config.js +19 -5
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +3 -2
- package/dist/content/agents.js +2 -2
- package/dist/content/examples.js +71 -62
- package/dist/content/hooks.d.ts +1 -0
- package/dist/content/hooks.js +145 -0
- package/dist/content/learnings.js +25 -5
- package/dist/content/meta-skill.js +12 -0
- package/dist/content/next-command.js +8 -0
- package/dist/content/observe.js +18 -0
- package/dist/content/session-hooks.js +1 -1
- package/dist/content/stage-schema.js +12 -2
- package/dist/content/status-command.d.ts +9 -0
- package/dist/content/status-command.js +132 -0
- package/dist/content/templates.js +18 -19
- package/dist/content/utility-skills.d.ts +6 -2
- package/dist/content/utility-skills.js +431 -3
- package/dist/delegation.d.ts +6 -0
- package/dist/delegation.js +12 -4
- package/dist/doctor.js +37 -1
- package/dist/flow-state.d.ts +16 -4
- package/dist/flow-state.js +50 -11
- package/dist/gate-evidence.d.ts +14 -0
- package/dist/gate-evidence.js +65 -3
- package/dist/harness-adapters.js +1 -0
- package/dist/install.d.ts +2 -1
- package/dist/install.js +107 -6
- package/dist/runs.d.ts +13 -1
- package/dist/runs.js +73 -7
- package/dist/types.d.ts +13 -0
- package/dist/types.js +13 -0
- package/package.json +1 -1
package/dist/content/examples.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
const STAGE_EXAMPLES = {
|
|
2
|
-
brainstorm:
|
|
2
|
+
brainstorm: `## Context
|
|
3
3
|
|
|
4
4
|
- **Project state:** Monorepo with CI pipeline using custom release scripts. Release checks are scattered across shell scripts with no shared validation logic.
|
|
5
5
|
- **Relevant existing code/patterns:** \`scripts/pre-publish.sh\` does metadata checks. \`src/release/\` has partial validation helpers.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Problem
|
|
8
8
|
|
|
9
9
|
- **What we're solving:** release checks are fragile and inconsistent between CI and local runs. Invalid metadata sometimes reaches npm publish.
|
|
10
10
|
- **Success criteria:** invalid release preconditions are caught before publish with explicit operator feedback, in both CI and local workflows.
|
|
11
11
|
- **Constraints:** no new runtime dependencies; must work within existing CI pipeline structure.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Clarifying Questions
|
|
14
14
|
|
|
15
15
|
| # | Question | Answer | Decision impact |
|
|
16
16
|
| --- | --- | --- | --- |
|
|
@@ -18,7 +18,7 @@ const STAGE_EXAMPLES = {
|
|
|
18
18
|
| 2 | Should the validation logic live in a reusable module or stay as shell scripts? | Reusable module. | Architecture: shared TypeScript module imported by CI and local tooling, not duplicated shell scripts. |
|
|
19
19
|
| 3 | For v1, prioritize rapid delivery or maximum configurability? | Rapid delivery. | Minimal deterministic validation surface; defer plugin/config system to v2. |
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
## Approaches
|
|
22
22
|
|
|
23
23
|
| Approach | Architecture | Trade-offs | Recommendation |
|
|
24
24
|
| --- | --- | --- | --- |
|
|
@@ -26,33 +26,33 @@ const STAGE_EXAMPLES = {
|
|
|
26
26
|
| B: Hardened shell scripts | Keep existing script approach, add stricter checks and error messages. | Lowest effort. Weak reuse, CI/local divergence risk grows over time. | Viable fallback if TS module is blocked. |
|
|
27
27
|
| C: Full release framework | New release orchestrator with plugin system, config files, rollback commands. | Maximum flexibility. High risk, delivery delay, over-engineered for current needs. | Not recommended for v1. |
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
## Selected Direction
|
|
30
30
|
|
|
31
31
|
- **Approach:** A — Reusable validation module
|
|
32
32
|
- **Rationale:** shared TS module gives consistent behavior in CI and local, avoids script duplication, and stays within the no-new-dependency constraint.
|
|
33
33
|
- **Approval:** approved
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
## Design
|
|
36
36
|
|
|
37
37
|
- **Architecture:** single \`release-validator\` module in \`src/release/\` exporting typed check functions. CI script and local CLI both import and run the same checks.
|
|
38
38
|
- **Key components:** \`validateMetadata()\`, \`validateChangelog()\`, \`validateVersion()\` — each returns a typed result with error details. A \`runAll()\` orchestrator runs checks and exits non-zero on any failure.
|
|
39
39
|
- **Data flow:** package.json + CHANGELOG.md → validator module → structured result → CI/CLI renders human-readable report.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
## Assumptions and Open Questions
|
|
42
42
|
|
|
43
43
|
- **Assumptions:** CI remains the primary execution path; existing release metadata files remain the source of truth; v1 prioritizes determinism over customization.
|
|
44
44
|
- **Open questions:** What exact rollback sequence for failed publish? Should status output include machine-readable JSON alongside markdown?
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
## Notes for the next stage
|
|
47
47
|
|
|
48
48
|
Carry the no-new-dependency constraint and hard-block behavior directly into scope in/out boundaries.`,
|
|
49
|
-
scope:
|
|
49
|
+
scope: `## Scope contract
|
|
50
50
|
|
|
51
51
|
**Mode selected:** SELECTIVE EXPANSION
|
|
52
52
|
**Default heuristic used:** feature enhancement -> selective
|
|
53
53
|
**Mode-specific analysis result:** hold-scope baseline accepted first; one expansion accepted (degraded-state UX), one deferred (real-time channel upgrade).
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
## Prime Directives (applied)
|
|
56
56
|
|
|
57
57
|
- Zero silent failures: every delivery failure maps to a visible degraded state.
|
|
58
58
|
- Named error surfaces: stream disconnect, auth drift, and publisher timeout are explicit.
|
|
@@ -60,11 +60,11 @@ Carry the no-new-dependency constraint and hard-block behavior directly into sco
|
|
|
60
60
|
- Interaction edge cases in scope: double-open panel, reconnect after sleep, stale tab state.
|
|
61
61
|
- Observability in scope: stream error counter, publish-to-visible lag metric, and alert threshold.
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
## Premise challenge result
|
|
64
64
|
|
|
65
65
|
The original premise (“add notifications”) was reframed to **“ensure users know when an action requires follow-up”**, which expands the solution space beyond toast spam to include durable inbox items, empty states, and recovery paths when delivery fails.
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
## Dream State Mapping
|
|
68
68
|
|
|
69
69
|
| Stage | Statement |
|
|
70
70
|
| --- | --- |
|
|
@@ -73,7 +73,7 @@ The original premise (“add notifications”) was reframed to **“ensure users
|
|
|
73
73
|
| **12-MONTH IDEAL** | Unified notification center with reliable multi-channel fan-out and user-level routing preferences. |
|
|
74
74
|
| **Alignment verdict** | Aligned: this scope builds the durability foundation without prematurely committing to channel expansion. |
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
## Mode-Specific Analysis
|
|
77
77
|
|
|
78
78
|
**Selected mode:** SELECTIVE EXPANSION
|
|
79
79
|
|
|
@@ -81,7 +81,7 @@ The original premise (“add notifications”) was reframed to **“ensure users
|
|
|
81
81
|
- **Expansion evaluated — degraded-state UX (accepted):** Adding an explicit "live updates paused" banner and polling fallback turns a reliability gap into a visible, recoverable state. Low incremental effort (S), high user trust payoff.
|
|
82
82
|
- **Expansion evaluated — real-time channel upgrade (deferred):** WebSocket channel provides lower latency but requires new infra (connection pool, auth handshake). Not justified for current load; deferred to post-v1 validation.
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
## Implementation Alternatives
|
|
85
85
|
|
|
86
86
|
| Option | Summary | Effort (S/M/L/XL) | Risk | Pros | Cons | Reuses |
|
|
87
87
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
@@ -89,7 +89,7 @@ The original premise (“add notifications”) was reframed to **“ensure users
|
|
|
89
89
|
| **B (recommended)** | SSE live updates + REST fallback snapshot | M | Med | Better timeliness, graceful degradation | Requires reconnect handling | Existing event publisher + REST path |
|
|
90
90
|
| **C (ideal architecture)** | Event bus + WebSocket channel + feed projection | XL | High | Strong long-term scalability | Overbuilt for current demand | Partial reuse of publisher only |
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
## Temporal Interrogation
|
|
93
93
|
|
|
94
94
|
| Time slice | Likely decision pressure | Lock now or defer? | Reason |
|
|
95
95
|
| --- | --- | --- | --- |
|
|
@@ -98,7 +98,7 @@ The original premise (“add notifications”) was reframed to **“ensure users
|
|
|
98
98
|
| **HOUR 4-5 (integration)** | Handling gaps between snapshot and stream cursor | **Lock now** | Prevent silent data loss during reconnect windows |
|
|
99
99
|
| **HOUR 6+ (polish/tests)** | Banner copy tone and polling cadence tuning | **Defer** | Safe to iterate after baseline reliability is proven |
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
## In scope / out of scope / deferred
|
|
102
102
|
|
|
103
103
|
| Category | Items |
|
|
104
104
|
| --- | --- |
|
|
@@ -106,29 +106,29 @@ The original premise (“add notifications”) was reframed to **“ensure users
|
|
|
106
106
|
| **Out of scope** | Email/SMS/push providers; marketing campaigns; per-user notification preferences beyond on/off |
|
|
107
107
|
| **Deferred** | WebSocket channel; rich media attachments in notifications; full-text search across historical events |
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
## Discretion Areas
|
|
110
110
|
|
|
111
111
|
- Client-side badge rendering strategy (optimistic vs server-confirmed) is implementation discretion.
|
|
112
112
|
- Polling fallback backoff curve is implementation discretion if degraded-state UX remains explicit.
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
## Error & Rescue Registry (sample entry)
|
|
115
115
|
|
|
116
116
|
| Capability | Failure mode | Detection | Fallback |
|
|
117
117
|
| --- | --- | --- | --- |
|
|
118
118
|
| Event delivery | SSE connection drops mid-session | Client \`EventSource\` error event + heartbeat timeout | Fall back to REST polling every 30s until SSE reconnect succeeds; show subtle “live updates paused” banner |
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
## Completion Dashboard
|
|
121
121
|
|
|
122
122
|
- Checklist findings: 9/9 complete (complex path)
|
|
123
123
|
- Resolved decisions count: 7
|
|
124
124
|
- Unresolved decisions: None
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
## Scope Summary
|
|
127
127
|
|
|
128
128
|
- Accepted scope: durable feed + SSE + explicit degraded UX.
|
|
129
129
|
- Deferred: WebSocket channel and rich-media/search enhancements.
|
|
130
130
|
- Explicitly excluded: outbound channels and marketing workflows for v1.`,
|
|
131
|
-
design:
|
|
131
|
+
design: `## Codebase Investigation (blast-radius files)
|
|
132
132
|
|
|
133
133
|
| File | Current responsibility | Patterns discovered |
|
|
134
134
|
| --- | --- | --- |
|
|
@@ -139,7 +139,7 @@ The original premise (“add notifications”) was reframed to **“ensure users
|
|
|
139
139
|
|
|
140
140
|
Discovery: existing EventEmitter-based bus has no durability — notifications must add persistence layer on top, not replace the bus.
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
## Search Before Building (sample result)
|
|
143
143
|
|
|
144
144
|
| Layer | Label | What to reuse first |
|
|
145
145
|
| --- | --- | --- |
|
|
@@ -147,7 +147,7 @@ Discovery: existing EventEmitter-based bus has no durability — notifications m
|
|
|
147
147
|
| Layer 2 | existing codebase | Existing auth middleware, existing API client wrapper, existing feature flags helper |
|
|
148
148
|
| Layer 3 | npm | A small, well-maintained SSE helper (only if Layer 1–2 cannot cover framing/reconnect ergonomics) |
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
## Architecture Diagram (mandatory)
|
|
151
151
|
|
|
152
152
|
\`\`\`
|
|
153
153
|
┌─────────────┐ ┌──────────────┐ ┌────────────────┐
|
|
@@ -163,7 +163,7 @@ Discovery: existing EventEmitter-based bus has no durability — notifications m
|
|
|
163
163
|
|
|
164
164
|
Data flow: Gateway → Service (validate + enrich) → Publisher (fan-out) → Queue (persist) → Read Model (project).
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
## What Already Exists
|
|
167
167
|
|
|
168
168
|
| Sub-problem | Existing code/library | Layer | Reuse decision |
|
|
169
169
|
| --- | --- | --- | --- |
|
|
@@ -172,7 +172,7 @@ Data flow: Gateway → Service (validate + enrich) → Publisher (fan-out) → Q
|
|
|
172
172
|
| SSE framing | None | Layer 3 | Evaluate \`better-sse\` npm package |
|
|
173
173
|
| Notification schema | None | — | New: define in \`src/schemas/notification.ts\` |
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
## Failure Mode Table
|
|
176
176
|
|
|
177
177
|
| Failure | Trigger | Detection | Mitigation | User impact |
|
|
178
178
|
| --- | --- | --- | --- | --- |
|
|
@@ -180,13 +180,13 @@ Data flow: Gateway → Service (validate + enrich) → Publisher (fan-out) → Q
|
|
|
180
180
|
| Duplicate publish | Retry after timeout | Dedupe key check in outbox | Upsert with idempotency key | None (transparent) |
|
|
181
181
|
| Queue backpressure | Spike >1000 events/s | Queue depth metric alarm | Back-pressure signal to publisher, shed non-critical events | Delayed delivery of low-priority notifications |
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
## Test Strategy
|
|
184
184
|
|
|
185
185
|
- **Unit:** validator functions, dedupe-key logic, event schema factories — target 90%+ line coverage.
|
|
186
186
|
- **Integration:** publisher → outbox → read-model pipeline via in-memory DB; SSE reconnect with simulated drops.
|
|
187
187
|
- **E2E:** one happy-path browser test (publish → feed visible) and one degraded-path test (SSE down → REST fallback + banner).
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
## Performance Budget
|
|
190
190
|
|
|
191
191
|
| Critical path | Metric | Target | Measurement method |
|
|
192
192
|
| --- | --- | --- | --- |
|
|
@@ -194,13 +194,13 @@ Data flow: Gateway → Service (validate + enrich) → Publisher (fan-out) → Q
|
|
|
194
194
|
| Feed snapshot load | p99 response time | ≤ 200 ms | Load test with 1 000 items per user |
|
|
195
195
|
| SSE reconnect | Time to first event after drop | ≤ 3 s | Simulated disconnect in integration suite |
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
## NOT in scope
|
|
198
198
|
|
|
199
199
|
- Outbound channels (email, push, SMS) — deferred to v2.
|
|
200
200
|
- Admin notification management UI — separate workstream.
|
|
201
201
|
- Notification preferences / mute rules — requires user settings redesign.
|
|
202
202
|
|
|
203
|
-
|
|
203
|
+
## Parallelization Strategy
|
|
204
204
|
|
|
205
205
|
| Module | Depends on | Parallel lane | Conflict risk |
|
|
206
206
|
| --- | --- | --- | --- |
|
|
@@ -208,18 +208,18 @@ Data flow: Gateway → Service (validate + enrich) → Publisher (fan-out) → Q
|
|
|
208
208
|
| Publisher + outbox (T2) | T1 | Lane A | None |
|
|
209
209
|
| Client feed + SSE (T3) | T1, T2 | Lane B (after T1) | Shared event type definitions |
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
## Unresolved Decisions
|
|
212
212
|
|
|
213
213
|
| Decision | Status | Options | Missing info | Default if unanswered |
|
|
214
214
|
| --- | --- | --- | --- | --- |
|
|
215
215
|
| Feed storage model | OPEN | (A) append-only event log, (B) mutable rows, (C) hybrid | Load testing results on read patterns | (A) append-only — safest for audit trail |
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
## Interface sketch (non-binding)
|
|
218
218
|
|
|
219
219
|
- **Client → server:** \`GET /api/me/notifications/snapshot?limit=50\` plus optional cursor parameters (if adopted).
|
|
220
220
|
- **Server → client:** \`GET /api/me/notifications/stream\` as SSE with periodic heartbeats.
|
|
221
221
|
|
|
222
|
-
|
|
222
|
+
## Completion Dashboard
|
|
223
223
|
|
|
224
224
|
| Review Section | Status | Issues |
|
|
225
225
|
| --- | --- | --- |
|
|
@@ -231,10 +231,10 @@ Data flow: Gateway → Service (validate + enrich) → Publisher (fan-out) → Q
|
|
|
231
231
|
|
|
232
232
|
**Decisions made:** 4 | **Unresolved:** 1 (feed storage model)
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
## Quality bar for this stage
|
|
235
235
|
|
|
236
236
|
Design output should be **reviewable by someone who did not attend brainstorming**: they can trace from constraints → components → open decisions without reading code.`,
|
|
237
|
-
spec:
|
|
237
|
+
spec: `## Acceptance Criteria
|
|
238
238
|
|
|
239
239
|
| ID | Criterion (observable/measurable/falsifiable) | Design Decision Ref |
|
|
240
240
|
| --- | --- | --- |
|
|
@@ -242,7 +242,7 @@ Design output should be **reviewable by someone who did not attend brainstorming
|
|
|
242
242
|
| AC-2 | Given the same logical notification is published twice with the same dedupe key, when the client processes the stream, the feed contains exactly one visible item for that key. | Architecture: dedupe-key in event schema |
|
|
243
243
|
| AC-3 | Given the live connection is unavailable, when the user opens the notifications panel, the UI shows a non-blocking "live updates paused" banner and loads the latest snapshot via REST within 2 seconds. | Architecture: REST fallback + degraded UX |
|
|
244
244
|
|
|
245
|
-
|
|
245
|
+
## Edge Cases
|
|
246
246
|
|
|
247
247
|
| Criterion ID | Boundary case | Error case |
|
|
248
248
|
| --- | --- | --- |
|
|
@@ -250,12 +250,12 @@ Design output should be **reviewable by someone who did not attend brainstorming
|
|
|
250
250
|
| AC-2 | Two events with identical dedupe key arrive within same SSE frame (boundary: only one row rendered). | Dedupe-key field missing — reject event at publisher and log error. |
|
|
251
251
|
| AC-3 | SSE disconnects after exactly 30 s heartbeat timeout (boundary: banner appears within 1 s of timeout). | REST snapshot endpoint returns 500 — panel shows "unable to load" with retry button. |
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
## Constraints and Assumptions
|
|
254
254
|
|
|
255
255
|
- **Constraints:** Max feed size 1 000 items per user. SSE heartbeat interval 30 s (server-side). REST snapshot p99 \u2264 200 ms. No new runtime dependencies.
|
|
256
256
|
- **Assumptions:** Users have a single active session at a time for v1. Existing auth middleware provides user context. Event publisher is single-writer per user.
|
|
257
257
|
|
|
258
|
-
|
|
258
|
+
## Testability Map
|
|
259
259
|
|
|
260
260
|
| Criterion ID | Verification approach | Command/manual steps |
|
|
261
261
|
| --- | --- | --- |
|
|
@@ -263,11 +263,11 @@ Design output should be **reviewable by someone who did not attend brainstorming
|
|
|
263
263
|
| AC-2 | Unit test: publish same dedupe key twice \u2192 assert single row in feed store. | \`pnpm vitest run tests/unit/dedupe-feed.test.ts\` |
|
|
264
264
|
| AC-3 | E2E test: kill SSE transport \u2192 assert banner visible + REST snapshot loads. | \`pnpm playwright test tests/e2e/degraded-mode.spec.ts\` |
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
## Approval
|
|
267
267
|
|
|
268
268
|
- Approved by: user
|
|
269
269
|
- Date: 2026-04-14`,
|
|
270
|
-
plan:
|
|
270
|
+
plan: `## Dependency Graph
|
|
271
271
|
|
|
272
272
|
\`\`\`
|
|
273
273
|
T-1 ──▶ T-2 ──▶ T-3
|
|
@@ -277,7 +277,7 @@ T-1 ──▶ T-2 ──▶ T-3
|
|
|
277
277
|
|
|
278
278
|
Parallel opportunity: T-1 is a prerequisite for both T-2 and T-3 (T-3 also needs T-2).
|
|
279
279
|
|
|
280
|
-
|
|
280
|
+
## Dependency Waves
|
|
281
281
|
|
|
282
282
|
#### Wave 1 (foundation)
|
|
283
283
|
- Task IDs: T-1
|
|
@@ -295,7 +295,7 @@ Parallel opportunity: T-1 is a prerequisite for both T-2 and T-3 (T-3 also needs
|
|
|
295
295
|
|
|
296
296
|
Execution rule: complete and verify each wave before starting the next wave.
|
|
297
297
|
|
|
298
|
-
|
|
298
|
+
## Task List
|
|
299
299
|
|
|
300
300
|
| Task ID | Description | Acceptance criterion | Verification command | Effort |
|
|
301
301
|
| --- | --- | --- | --- | --- |
|
|
@@ -303,7 +303,7 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
303
303
|
| T-2 | Implement publisher + outbox write path | AC-1: integration test (happy path publish) | \`\`\`pnpm vitest run tests/integration/publisher.test.ts\`\`\` |
|
|
304
304
|
| T-3 | Implement client feed + SSE subscribe + REST fallback | AC-1, AC-2, AC-3: e2e tests including degraded mode | \`\`\`pnpm playwright test tests/e2e/notification-feed.spec.ts\`\`\` |
|
|
305
305
|
|
|
306
|
-
|
|
306
|
+
## Acceptance Mapping
|
|
307
307
|
|
|
308
308
|
| Criterion ID | Task IDs |
|
|
309
309
|
| --- | --- |
|
|
@@ -311,17 +311,17 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
311
311
|
| AC-2 (idempotency) | T-1, T-2 |
|
|
312
312
|
| AC-3 (failure visibility) | T-3 |
|
|
313
313
|
|
|
314
|
-
|
|
314
|
+
## Risk Assessment
|
|
315
315
|
|
|
316
316
|
| Task/Wave | Risk | Likelihood | Impact | Mitigation |
|
|
317
317
|
| --- | --- | --- | --- | --- |
|
|
318
318
|
| T-3 (Wave 3) | SSE reconnect logic complex | Medium | High | Spike reconnect in isolation before integrating with feed UI |
|
|
319
319
|
| Wave 2 → 3 | Publisher API contract may shift | Low | Medium | Pin contract in T-1 schema; T-2 integration test validates |
|
|
320
320
|
|
|
321
|
-
|
|
321
|
+
## WAIT_FOR_CONFIRM
|
|
322
322
|
- Status: pending
|
|
323
323
|
- Confirmed by:`,
|
|
324
|
-
tdd:
|
|
324
|
+
tdd: `## RED Evidence
|
|
325
325
|
|
|
326
326
|
| Slice | Test name | Command | Failure output summary |
|
|
327
327
|
| --- | --- | --- | --- |
|
|
@@ -329,7 +329,7 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
329
329
|
| S-2 (publisher outbox) | publishes event to outbox with dedupe key | \`\`\`pnpm vitest run tests/integration/publisher.test.ts\`\`\` | publishToOutbox is not a function |
|
|
330
330
|
| S-3 (client feed + fallback) | shows notification within 5s via SSE | \`\`\`pnpm playwright test tests/e2e/notification-feed.spec.ts\`\`\` | Element [data-testid="feed-item"] not found |
|
|
331
331
|
|
|
332
|
-
|
|
332
|
+
## Acceptance Mapping
|
|
333
333
|
|
|
334
334
|
| Slice | Plan task ID | Spec criterion ID |
|
|
335
335
|
| --- | --- | --- |
|
|
@@ -337,7 +337,7 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
337
337
|
| S-2 | T-2 | AC-1 |
|
|
338
338
|
| S-3 | T-3 | AC-1, AC-2, AC-3 |
|
|
339
339
|
|
|
340
|
-
|
|
340
|
+
## Failure Analysis
|
|
341
341
|
|
|
342
342
|
| Slice | Expected missing behavior | Actual failure reason |
|
|
343
343
|
| --- | --- | --- |
|
|
@@ -345,22 +345,22 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
345
345
|
| S-2 | publishToOutbox function not implemented | Function not found — correct: write path missing |
|
|
346
346
|
| S-3 | Feed UI not rendered, SSE not connected | DOM element missing — correct: client component not built |
|
|
347
347
|
|
|
348
|
-
|
|
348
|
+
## GREEN Evidence
|
|
349
349
|
|
|
350
350
|
- Full suite command: \`\`\`pnpm vitest run && pnpm playwright test\`\`\`
|
|
351
351
|
- Full suite result: 47 tests passed (3 new + 44 existing), 0 failed, 0 skipped
|
|
352
352
|
|
|
353
|
-
|
|
353
|
+
## REFACTOR Notes
|
|
354
354
|
|
|
355
355
|
- What changed: Extracted \`\`\`mergeLatestByDedupeKey\`\`\` helper from inline loop in \`\`\`summarizeDedupedFeed\`\`\`; moved SSE reconnect logic into \`\`\`useSSEConnection\`\`\` hook.
|
|
356
356
|
- Why: Dedupe merge logic is reused by both publisher and client; reconnect logic was duplicated across components.
|
|
357
357
|
- Behavior preserved: Full suite re-run confirms 47/47 pass after refactor.
|
|
358
358
|
|
|
359
|
-
|
|
359
|
+
## Traceability
|
|
360
360
|
|
|
361
361
|
- Plan task IDs: T-1, T-2, T-3
|
|
362
362
|
- Spec criterion IDs: AC-1, AC-2, AC-3`,
|
|
363
|
-
review:
|
|
363
|
+
review: `## Layer 1 Verdict
|
|
364
364
|
|
|
365
365
|
| Criterion | Verdict | Evidence |
|
|
366
366
|
| --- | --- | --- |
|
|
@@ -368,7 +368,7 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
368
368
|
| AC-2: Dedupe — one visible item per key | PARTIAL | Unit tests cover publisher dedupe; UI merge path lacks test for race reordering (\`feedStore.test.ts\` missing case) |
|
|
369
369
|
| AC-3: Degraded mode + REST snapshot | PASS | \`NotificationsPanel.tsx:112-140\` renders banner + calls snapshot endpoint |
|
|
370
370
|
|
|
371
|
-
|
|
371
|
+
## Layer 2 Findings
|
|
372
372
|
|
|
373
373
|
| ID | Severity | Category | Description | Status |
|
|
374
374
|
| --- | --- | --- | --- | --- |
|
|
@@ -376,12 +376,12 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
376
376
|
| R-2 | Important | performance | \`feedStore.merge()\` does full-array scan on every SSE event; O(n) per event where n is feed length. | open |
|
|
377
377
|
| R-3 | Suggestion | architecture | SSE reconnect logic duplicated across \`useNotifications\` and \`usePresence\`; extract shared hook. | open |
|
|
378
378
|
|
|
379
|
-
|
|
379
|
+
## Review Army Contract
|
|
380
380
|
|
|
381
381
|
- See \`07-review-army.json\`
|
|
382
382
|
- Reconciliation summary: 1 duplicate collapsed (R-1 reported by spec-reviewer and code-reviewer), 0 conflicts
|
|
383
383
|
|
|
384
|
-
|
|
384
|
+
## Review Readiness Dashboard
|
|
385
385
|
|
|
386
386
|
- Layer 1 complete: yes (3/3 criteria)
|
|
387
387
|
- Layer 2 complete: yes (5 sections reviewed)
|
|
@@ -389,16 +389,16 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
389
389
|
- Open critical blockers: 1 (R-1)
|
|
390
390
|
- Ship recommendation: BLOCKED until R-1 resolved
|
|
391
391
|
|
|
392
|
-
|
|
392
|
+
## Severity Summary
|
|
393
393
|
|
|
394
394
|
- Critical: 1
|
|
395
395
|
- Important: 1
|
|
396
396
|
- Suggestion: 1
|
|
397
397
|
|
|
398
|
-
|
|
398
|
+
## Final Verdict
|
|
399
399
|
|
|
400
400
|
- BLOCKED`,
|
|
401
|
-
ship:
|
|
401
|
+
ship: `## Preflight Results
|
|
402
402
|
|
|
403
403
|
- Review verdict: APPROVED_WITH_CONCERNS (R-1 resolved, R-2 accepted as known debt)
|
|
404
404
|
- Build: pass (\`pnpm build\` succeeds)
|
|
@@ -407,25 +407,25 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
407
407
|
- Type-check: pass (\`pnpm typecheck\` clean)
|
|
408
408
|
- Working tree clean: yes (\`git status\` shows no uncommitted changes)
|
|
409
409
|
|
|
410
|
-
|
|
410
|
+
## Release Notes
|
|
411
411
|
|
|
412
412
|
- **Added:** In-app notification feed with SSE updates and REST fallback snapshotting (AC-1, AC-3).
|
|
413
413
|
- **Changed:** Notification payloads now include a stable dedupe key for idempotent rendering (AC-2).
|
|
414
414
|
- **Fixed:** Panel no longer drops the newest item when reconnecting after sleep/resume.
|
|
415
415
|
- **Breaking changes:** None.
|
|
416
416
|
|
|
417
|
-
|
|
417
|
+
## Rollback Plan
|
|
418
418
|
|
|
419
419
|
- Trigger conditions: error rate on \`/notifications/stream\` exceeds 5% for >5 minutes, or p95 publish-to-visible lag exceeds 10s.
|
|
420
420
|
- Rollback steps: \`git revert <merge-sha> && git push origin main\` then redeploy; if DB migrations shipped, run \`2026_04_12_notifications_cursor_down.sql\` before traffic.
|
|
421
421
|
- Verification steps: confirm error rate returns to pre-release baseline within 10 minutes; smoke-test feed panel manually.
|
|
422
422
|
|
|
423
|
-
|
|
423
|
+
## Monitoring
|
|
424
424
|
|
|
425
425
|
- Metrics/logs to watch: error rate on \`/notifications/stream\` and snapshot endpoint for 24h; p95 publish-to-visible lag via metrics dashboard.
|
|
426
426
|
- Risk note (if no monitoring): N/A — monitoring is in place.
|
|
427
427
|
|
|
428
|
-
|
|
428
|
+
## Finalization
|
|
429
429
|
|
|
430
430
|
- Selected enum: FINALIZE_OPEN_PR
|
|
431
431
|
- Selected label: B
|
|
@@ -436,5 +436,14 @@ export function stageExamples(stage) {
|
|
|
436
436
|
const examples = STAGE_EXAMPLES[stage];
|
|
437
437
|
if (!examples)
|
|
438
438
|
return "";
|
|
439
|
-
return
|
|
439
|
+
return [
|
|
440
|
+
"## Examples",
|
|
441
|
+
"",
|
|
442
|
+
"Concrete artifact samples. These mirror the exact heading levels agents must use when authoring the stage artifact (all H2 `##` sections), so they are presented inside a markdown fence to avoid collapsing into the SKILL outline.",
|
|
443
|
+
"",
|
|
444
|
+
"```markdown",
|
|
445
|
+
examples,
|
|
446
|
+
"```",
|
|
447
|
+
""
|
|
448
|
+
].join("\n");
|
|
440
449
|
}
|
package/dist/content/hooks.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface HookRuntimeOptions {
|
|
|
11
11
|
export declare const RUNTIME_SHELL_DETECT_ROOT = "HARNESS=\"codex\"\nif [ -n \"${CLAUDE_PROJECT_DIR:-}\" ]; then\n HARNESS=\"claude\"\nelif [ -n \"${CURSOR_PROJECT_DIR:-}\" ] || [ -n \"${CURSOR_PROJECT_ROOT:-}\" ]; then\n HARNESS=\"cursor\"\nelif [ -n \"${OPENCODE_PROJECT_DIR:-}\" ] || [ -n \"${OPENCODE_PROJECT_ROOT:-}\" ]; then\n HARNESS=\"opencode\"\nfi\n\nROOT=\"\"\nfor candidate in \"${CCLAW_PROJECT_ROOT:-}\" \"${CLAUDE_PROJECT_DIR:-}\" \"${CURSOR_PROJECT_DIR:-}\" \"${CURSOR_PROJECT_ROOT:-}\" \"${OPENCODE_PROJECT_DIR:-}\" \"${OPENCODE_PROJECT_ROOT:-}\" \"${PWD:-}\"; do\n if [ -n \"$candidate\" ] && [ -d \"$candidate/.cclaw\" ]; then\n ROOT=\"$candidate\"\n break\n fi\ndone\nif [ -z \"$ROOT\" ]; then\n ROOT=\"${CCLAW_PROJECT_ROOT:-${CLAUDE_PROJECT_DIR:-${CURSOR_PROJECT_DIR:-${CURSOR_PROJECT_ROOT:-${OPENCODE_PROJECT_DIR:-${OPENCODE_PROJECT_ROOT:-${PWD}}}}}}}\"\nfi";
|
|
12
12
|
export declare function sessionStartScript(_options?: HookRuntimeOptions): string;
|
|
13
13
|
export declare function stopCheckpointScript(): string;
|
|
14
|
+
export declare function preCompactScript(): string;
|
|
14
15
|
export { claudeHooksJsonWithObservation as claudeHooksJson } from "./observe.js";
|
|
15
16
|
export { cursorHooksJsonWithObservation as cursorHooksJson } from "./observe.js";
|
|
16
17
|
export { codexHooksJsonWithObservation as codexHooksJson } from "./observe.js";
|
package/dist/content/hooks.js
CHANGED
|
@@ -616,6 +616,151 @@ case "$HARNESS" in
|
|
|
616
616
|
esac
|
|
617
617
|
`;
|
|
618
618
|
}
|
|
619
|
+
export function preCompactScript() {
|
|
620
|
+
return `#!/usr/bin/env bash
|
|
621
|
+
# cclaw pre-compact hook — generated by cclaw sync
|
|
622
|
+
# Persists a session digest before the harness compacts/clears context, so the
|
|
623
|
+
# next session-start hook can restore the most important state without the agent
|
|
624
|
+
# having to re-derive it from scratch.
|
|
625
|
+
set -uo pipefail
|
|
626
|
+
|
|
627
|
+
${DETECT_ROOT}
|
|
628
|
+
|
|
629
|
+
INPUT=$(cat 2>/dev/null || echo '{}')
|
|
630
|
+
|
|
631
|
+
STATE_DIR="$ROOT/${RUNTIME_ROOT}/state"
|
|
632
|
+
STATE_FILE="$STATE_DIR/flow-state.json"
|
|
633
|
+
DELEGATION_FILE="$STATE_DIR/delegation-log.json"
|
|
634
|
+
KNOWLEDGE_FILE="$ROOT/${RUNTIME_ROOT}/knowledge.md"
|
|
635
|
+
DIGEST_FILE="$STATE_DIR/session-digest.md"
|
|
636
|
+
DIGEST_TMP="$STATE_DIR/session-digest.md.tmp.$$"
|
|
637
|
+
|
|
638
|
+
mkdir -p "$STATE_DIR" 2>/dev/null || true
|
|
639
|
+
|
|
640
|
+
cleanup_digest_tmp() {
|
|
641
|
+
rm -f "$DIGEST_TMP" 2>/dev/null || true
|
|
642
|
+
}
|
|
643
|
+
trap cleanup_digest_tmp EXIT INT TERM
|
|
644
|
+
|
|
645
|
+
STAGE="none"
|
|
646
|
+
TRACK="standard"
|
|
647
|
+
COMPLETED="0"
|
|
648
|
+
SKIPPED=""
|
|
649
|
+
ACTIVE_RUN="none"
|
|
650
|
+
PASSED_GATES=""
|
|
651
|
+
BLOCKED_GATES=""
|
|
652
|
+
|
|
653
|
+
if [ -f "$STATE_FILE" ]; then
|
|
654
|
+
if command -v jq >/dev/null 2>&1; then
|
|
655
|
+
STAGE=$(jq -r '.currentStage // "none"' "$STATE_FILE" 2>/dev/null || echo "none")
|
|
656
|
+
TRACK=$(jq -r '.track // "standard"' "$STATE_FILE" 2>/dev/null || echo "standard")
|
|
657
|
+
COMPLETED=$(jq -r '(.completedStages // []) | length' "$STATE_FILE" 2>/dev/null || echo "0")
|
|
658
|
+
SKIPPED=$(jq -r '(.skippedStages // []) | join(",")' "$STATE_FILE" 2>/dev/null || echo "")
|
|
659
|
+
ACTIVE_RUN=$(jq -r '.activeRunId // "none"' "$STATE_FILE" 2>/dev/null || echo "none")
|
|
660
|
+
PASSED_GATES=$(jq -r --arg stage "$STAGE" '(.stageGates[$stage].passed // []) | join(",")' "$STATE_FILE" 2>/dev/null || echo "")
|
|
661
|
+
BLOCKED_GATES=$(jq -r --arg stage "$STAGE" '(.stageGates[$stage].blocked // []) | join(",")' "$STATE_FILE" 2>/dev/null || echo "")
|
|
662
|
+
elif command -v python3 >/dev/null 2>&1; then
|
|
663
|
+
OUTPUT=$(python3 - "$STATE_FILE" <<'PY'
|
|
664
|
+
import json, sys
|
|
665
|
+
try:
|
|
666
|
+
with open(sys.argv[1], "r", encoding="utf-8") as fh:
|
|
667
|
+
data = json.load(fh)
|
|
668
|
+
except Exception:
|
|
669
|
+
data = {}
|
|
670
|
+
stage = data.get("currentStage") or "none"
|
|
671
|
+
track = data.get("track") or "standard"
|
|
672
|
+
completed = data.get("completedStages") or []
|
|
673
|
+
skipped = data.get("skippedStages") or []
|
|
674
|
+
run = data.get("activeRunId") or "none"
|
|
675
|
+
gates = (data.get("stageGates") or {}).get(stage) or {}
|
|
676
|
+
passed = gates.get("passed") or []
|
|
677
|
+
blocked = gates.get("blocked") or []
|
|
678
|
+
print(stage)
|
|
679
|
+
print(track)
|
|
680
|
+
print(len(completed) if isinstance(completed, list) else 0)
|
|
681
|
+
print(",".join(skipped) if isinstance(skipped, list) else "")
|
|
682
|
+
print(run)
|
|
683
|
+
print(",".join(passed) if isinstance(passed, list) else "")
|
|
684
|
+
print(",".join(blocked) if isinstance(blocked, list) else "")
|
|
685
|
+
PY
|
|
686
|
+
)
|
|
687
|
+
{
|
|
688
|
+
IFS= read -r STAGE
|
|
689
|
+
IFS= read -r TRACK
|
|
690
|
+
IFS= read -r COMPLETED
|
|
691
|
+
IFS= read -r SKIPPED
|
|
692
|
+
IFS= read -r ACTIVE_RUN
|
|
693
|
+
IFS= read -r PASSED_GATES
|
|
694
|
+
IFS= read -r BLOCKED_GATES
|
|
695
|
+
} <<EOF
|
|
696
|
+
$OUTPUT
|
|
697
|
+
EOF
|
|
698
|
+
fi
|
|
699
|
+
fi
|
|
700
|
+
|
|
701
|
+
DELEGATION_PENDING=""
|
|
702
|
+
if [ -f "$DELEGATION_FILE" ] && command -v jq >/dev/null 2>&1; then
|
|
703
|
+
DELEGATION_PENDING=$(jq -r --arg stage "$STAGE" '
|
|
704
|
+
(.entries // [])
|
|
705
|
+
| map(select((.stage // "") == $stage and (.status // "") != "completed" and (.status // "") != "waived"))
|
|
706
|
+
| map(.agent // "unknown")
|
|
707
|
+
| unique
|
|
708
|
+
| join(",")
|
|
709
|
+
' "$DELEGATION_FILE" 2>/dev/null || echo "")
|
|
710
|
+
fi
|
|
711
|
+
|
|
712
|
+
KNOWLEDGE_TAIL=""
|
|
713
|
+
if [ -f "$KNOWLEDGE_FILE" ] && [ -s "$KNOWLEDGE_FILE" ]; then
|
|
714
|
+
KNOWLEDGE_TAIL=$(tail -n 12 "$KNOWLEDGE_FILE" 2>/dev/null || echo "")
|
|
715
|
+
fi
|
|
716
|
+
|
|
717
|
+
GIT_HEAD=""
|
|
718
|
+
GIT_BRANCH=""
|
|
719
|
+
GIT_DIRTY="unknown"
|
|
720
|
+
if command -v git >/dev/null 2>&1 && git -C "$ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
721
|
+
GIT_HEAD=$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo "")
|
|
722
|
+
GIT_BRANCH=$(git -C "$ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
723
|
+
if [ -n "$(git -C "$ROOT" status --porcelain 2>/dev/null)" ]; then
|
|
724
|
+
GIT_DIRTY="dirty"
|
|
725
|
+
else
|
|
726
|
+
GIT_DIRTY="clean"
|
|
727
|
+
fi
|
|
728
|
+
fi
|
|
729
|
+
|
|
730
|
+
TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "")
|
|
731
|
+
|
|
732
|
+
{
|
|
733
|
+
printf '# Session Digest\n'
|
|
734
|
+
printf '_Generated by pre-compact hook at %s_\n\n' "$TS"
|
|
735
|
+
printf '## Flow snapshot\n'
|
|
736
|
+
printf '- track: %s\n' "$TRACK"
|
|
737
|
+
printf '- current stage: %s\n' "$STAGE"
|
|
738
|
+
printf '- completed: %s stages\n' "$COMPLETED"
|
|
739
|
+
printf '- skipped: %s\n' "\${SKIPPED:-(none)}"
|
|
740
|
+
printf '- run: %s\n\n' "$ACTIVE_RUN"
|
|
741
|
+
printf '## Gates (current stage)\n'
|
|
742
|
+
printf '- passed: %s\n' "\${PASSED_GATES:-(none)}"
|
|
743
|
+
printf '- blocked: %s\n\n' "\${BLOCKED_GATES:-(none)}"
|
|
744
|
+
printf '## Outstanding delegations\n'
|
|
745
|
+
printf '- pending: %s\n\n' "\${DELEGATION_PENDING:-(none)}"
|
|
746
|
+
printf '## Git\n'
|
|
747
|
+
printf '- branch: %s\n' "\${GIT_BRANCH:-(unknown)}"
|
|
748
|
+
printf '- head: %s\n' "\${GIT_HEAD:-(unknown)}"
|
|
749
|
+
printf '- worktree: %s\n\n' "$GIT_DIRTY"
|
|
750
|
+
if [ -n "$KNOWLEDGE_TAIL" ]; then
|
|
751
|
+
printf '## Knowledge tail\n'
|
|
752
|
+
printf '%s\n' "$KNOWLEDGE_TAIL"
|
|
753
|
+
fi
|
|
754
|
+
} > "$DIGEST_TMP" 2>/dev/null || true
|
|
755
|
+
|
|
756
|
+
if [ -s "$DIGEST_TMP" ]; then
|
|
757
|
+
mv "$DIGEST_TMP" "$DIGEST_FILE" 2>/dev/null || rm -f "$DIGEST_TMP" 2>/dev/null || true
|
|
758
|
+
fi
|
|
759
|
+
|
|
760
|
+
trap - EXIT INT TERM
|
|
761
|
+
exit 0
|
|
762
|
+
`;
|
|
763
|
+
}
|
|
619
764
|
// ---------------------------------------------------------------------------
|
|
620
765
|
// hooks.json generators are defined in observe.ts (shared across harnesses).
|
|
621
766
|
// ---------------------------------------------------------------------------
|