pi-smart-compact 8.0.6 → 8.0.8

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 (47) hide show
  1. package/ARCHITECTURE.md +513 -0
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +70 -51
  4. package/SECURITY.md +8 -3
  5. package/dist/app/mode-policy.d.ts +0 -1
  6. package/dist/app/mode-policy.d.ts.map +1 -1
  7. package/dist/app/preflight.d.ts +16 -2
  8. package/dist/app/preflight.d.ts.map +1 -1
  9. package/dist/app/steps/extract.d.ts.map +1 -1
  10. package/dist/app/steps/state.d.ts.map +1 -1
  11. package/dist/app/steps/synthesize.d.ts.map +1 -1
  12. package/dist/app/steps/window.d.ts +3 -1
  13. package/dist/app/steps/window.d.ts.map +1 -1
  14. package/dist/constants.d.ts +18 -1
  15. package/dist/constants.d.ts.map +1 -1
  16. package/dist/domain/telemetry.d.ts +3 -0
  17. package/dist/domain/telemetry.d.ts.map +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +762 -549
  20. package/dist/infra/context-graph.d.ts.map +1 -1
  21. package/dist/infra/fs.d.ts +1 -1
  22. package/dist/infra/fs.d.ts.map +1 -1
  23. package/dist/infra/git.d.ts.map +1 -1
  24. package/dist/infra/session-identity.d.ts +4 -0
  25. package/dist/infra/session-identity.d.ts.map +1 -1
  26. package/dist/phases/verify.d.ts.map +1 -1
  27. package/dist/provider-eval.js +113 -22
  28. package/dist/provider-scenario-eval.js +153 -111
  29. package/dist/telemetry-report.js +138 -41
  30. package/dist/ui/dashboard-insights.d.ts.map +1 -1
  31. package/dist/ui/overlays.d.ts.map +1 -1
  32. package/dist/utils/cache.d.ts.map +1 -1
  33. package/dist/utils/extraction.d.ts +5 -0
  34. package/dist/utils/extraction.d.ts.map +1 -1
  35. package/dist/utils/file-needles.d.ts +7 -0
  36. package/dist/utils/file-needles.d.ts.map +1 -1
  37. package/dist/utils/fingerprint.d.ts +1 -1
  38. package/dist/utils/fingerprint.d.ts.map +1 -1
  39. package/dist/utils/pruning.d.ts.map +1 -1
  40. package/dist/utils/state.d.ts +4 -3
  41. package/dist/utils/state.d.ts.map +1 -1
  42. package/dist/utils/tokens.d.ts.map +1 -1
  43. package/docs/MIGRATING_TO_V8.md +30 -13
  44. package/docs/RELEASE.md +13 -10
  45. package/package.json +2 -1
  46. package/dist/app/explore-wrap.d.ts +0 -8
  47. package/dist/app/explore-wrap.d.ts.map +0 -1
@@ -0,0 +1,513 @@
1
+ # Architecture
2
+
3
+ System-level design of `pi-smart-compact`. This is the maintainer-facing
4
+ companion to the user-facing [`README.md`](./README.md).
5
+
6
+ > **Job:** not to produce a generic recap, but to preserve the agent's working
7
+ > state so the next turn can continue with minimal loss.
8
+
9
+ ## Design ideas
10
+
11
+ The design combines three ideas:
12
+
13
+ - **Agentic compaction** — let the system inspect the session, not just summarize it.
14
+ - **Kamradt-style chunking** — segment large conversations into coherent units before synthesis.
15
+ - **EESV** — **Extract → Explore → Synthesize → Verify**: facts first, synthesis second, verification last.
16
+
17
+ ## Integration surfaces
18
+
19
+ Registered in [`src/index.ts`](./src/index.ts). See the README for usage; this
20
+ section is about lifecycle.
21
+
22
+ | Surface | Lifecycle |
23
+ | --- | --- |
24
+ | `/smart-compact` | Manual command. Explainable target-first preflight or direct args. Bypasses the adaptive pressure gate, not yield/verification gates. |
25
+ | `session_before_compact` | Auto hook. Returns/stages a pending summary or runs under pressure; durable commit waits for matching `session_compact`. |
26
+ | `smart_compact` tool | Agent-callable. Prepares a pending summary; never compacts mid-turn. |
27
+ | `smart_recall` tool | Searches only the current project's bounded context graph; same session/branch ranks first. |
28
+ | `smart_save_memory` tool | Persists one explicit user-confirmed project fact after secret/PII scrubbing. |
29
+
30
+ A short-lived pending compaction is staged in the [`PendingSlot`](#pending-compaction-slot)
31
+ and handed to Pi when compaction is applied.
32
+
33
+ ### Host dependency boundary
34
+
35
+ Pi core modules and `typebox` are wildcard peers supplied by the running host;
36
+ they are neither bundled nor duplicated as versioned development dependencies.
37
+ The lockfile pins a reproducible local baseline, and `bun run compat:pi [version]`
38
+ validates another Pi release in an isolated temporary workspace.
39
+
40
+ ## Pipeline at a glance
41
+
42
+ ```mermaid
43
+ flowchart LR
44
+ A[Active Pi context] --> B[Keep recent tail]
45
+ B --> C[Extract deterministic facts]
46
+ C --> D{Complex enough?}
47
+ D -- No --> E[Single-pass synthesis]
48
+ D -- Yes --> F[Explore + segment]
49
+ F --> G[Chunked synthesis]
50
+ E --> H[Verify + repair]
51
+ G --> H
52
+ H --> I[Open loops + delta + state]
53
+ I --> Y{Target + ≥10% yield?}
54
+ Y -- No --> X[Reject; conversation unchanged]
55
+ Y -- Yes --> J[Pending compaction returned to Pi]
56
+ ```
57
+
58
+ The orchestrator ([`src/app/run-smart-compact.ts`](./src/app/run-smart-compact.ts))
59
+ threads a typed context through ten stages:
60
+
61
+ | # | Stage | Module | Transition |
62
+ | ---: | --- | --- | --- |
63
+ | 1 | prepare | `app/steps/prepare.ts` | config + auth + provider caps |
64
+ | 2 | window | `app/steps/window.ts` | pick the prefix to compact |
65
+ | 3 | recover | `app/steps/recover.ts` | restore log-truncated messages |
66
+ | 4 | tier | `app/steps/tier.ts` | choose none / light / full |
67
+ | 5 | extract | `app/steps/extract.ts` | prune + deterministic extraction + cache |
68
+ | 6 | synthesize | `app/steps/synthesize.ts` | single-pass or EESV |
69
+ | 7 | verify | `app/steps/verify.ts` | structural verify + repair |
70
+ | 8 | state | `app/steps/state.ts` + `domain/yield-gate.ts` | state/open loops/delta + final yield proof |
71
+ | 9 | persist | `app/steps/persist.ts` | stage pending, apply compaction |
72
+ | 10 | metrics | `app/steps/metrics.ts` | success / failure record |
73
+
74
+ ## The typed stage machine
75
+
76
+ [`src/app/run-context.ts`](./src/app/run-context.ts) models the pipeline context
77
+ as a **state machine of branded intersection types**. Each step accepts the
78
+ previous stage type and returns the next, so reordering or skipping a step is a
79
+ **compile-time error**, not a runtime crash:
80
+
81
+ ```
82
+ RcBase
83
+ → PreparedRc (after prepare)
84
+ → WindowedRc (after window)
85
+ → RecoveredRc (after recover)
86
+ → TieredRc (after tier)
87
+ → ExtractedRc (after extract)
88
+ → SynthesizedRc (after synthesize)
89
+ → VerifiedRc (after verify)
90
+ → StatedRc (after state)
91
+ ```
92
+
93
+ Each stage adds a `_prepared` / `_windowed` / … discriminator field that is
94
+ **never read at runtime** — it exists only to carry the type-level proof.
95
+ Mutation is preserved: a step mutates its input object and casts it to the
96
+ next stage (no per-step copy of ~30 fields). The final alias
97
+ `RunContext = StatedRc` keeps post-`buildState` consumers readable.
98
+
99
+ This is what lets `applyCompaction` read `rc.details` with zero `!` non-null
100
+ assertions: the type system proves `buildState` has run.
101
+
102
+ ## Core execution model
103
+
104
+ ### Entry and context gate
105
+
106
+ `src/index.ts` resolves models, parses command arguments, and routes work into
107
+ `runSmartCompact()`. Before any expensive work, the system checks context size
108
+ against the threshold in `src/constants.ts`. Auto / tool runs are skipped while
109
+ context is small; manual `/smart-compact` uses an absolute adaptive safety tail
110
+ rather than a percentage of large model windows. Its decision-card preflight
111
+ is built from the same config snapshot, calibrated estimator, adaptive profile,
112
+ active branch, and pure window planner as execution. It compares exactly Fast,
113
+ Balanced, and Thorough; `M` changes the summary route and replans all three,
114
+ while `D` reveals technical estimator/boundary details. A plan must meet the
115
+ tail target and at least 10% projected net savings before any model call. A
116
+ pending summary for the same session is reused instead of invoking the pipeline
117
+ again. `auto` is not a fourth policy: it selects one of the three from context
118
+ pressure and deterministic extraction risk.
119
+
120
+ Model routes are stage-specific but never inferred from mode. With no explicit
121
+ configuration, Explore, Synthesize, and Verify all use the selected Pi model.
122
+ `segmentationModel`, `summaryModel`, and `verificationModel` can independently
123
+ override those routes. `prepareRun()` resolves credentials once per distinct
124
+ provider/model route; call metrics preserve the actual route.
125
+
126
+ ### Keep window and preprocessing
127
+
128
+ `app/steps/window.ts` starts from Pi's compaction-aware
129
+ `buildContextEntries()` view, never the append-only session history, and builds
130
+ a content-free `CompactionWindowPlan` from the selected mode budget:
131
+
132
+ - **hard `toolCall` / `toolResult` guard** — never orphan a result from its call
133
+ - **soft recent-user/checkpoint/topical preferences** — retain raw only when the resulting suffix still fits the planned budget
134
+ - **yield contract** — projected replacement must meet its target and save at least 10% after reserving the summary budget
135
+
136
+ The same pure planner powers manual preflight and execution. A soft boundary is
137
+ recorded as relaxed rather than silently overriding the target. Long turns may
138
+ be summarized through their older prefix; if the nominal cut lands inside a
139
+ tool exchange, the planner either retains the complete pair within budget or
140
+ advances past it so the complete exchange is summarized. If no safe hard
141
+ boundary can meet the target, automatic/tool runs normally return control to
142
+ Pi's native compactor before any LLM call. An already-overflowed context is
143
+ the safety exception: measured usage is mapped across active messages and EESV
144
+ keeps chunked recovery instead of sending an oversized one-shot prompt to
145
+ native summarization. Manual runs use the profile's absolute adaptive tail, so
146
+ model-window size cannot turn an explicit command into a full-context no-op.
147
+
148
+ Before summarization the pipeline serializes the full selected conversation,
149
+ scrubs it, and writes that pre-prune backup; it then prunes redundant messages,
150
+ loads the previous verified summary plus bounded continuity state, checks the
151
+ incremental extraction cache, and loads the project fingerprint.
152
+
153
+ ### Extract
154
+
155
+ Primary: [`src/utils/extraction.ts`](./src/utils/extraction.ts). **Zero LLM calls.**
156
+
157
+ Deterministically pulls: modified / read / deleted files, tool and bash-like
158
+ errors, retry / resolution signals, explicit & implicit decisions, constraints
159
+ and preferences, heuristic topic segments, timeline events, the main goal, and
160
+ open loops. **This is the ground truth** that synthesis and verification trust.
161
+
162
+ ### Explore
163
+
164
+ Primary: [`src/phases/explore.ts`](./src/phases/explore.ts). Optional — runs only
165
+ in `thorough` mode or when `auto` selects that policy from deterministic risk. The model inspects
166
+ the conversation through a small toolset: message ranges, conversation search,
167
+ recent user messages, local context around an index, file-change lookups, and
168
+ error chains. Tool support is runtime-probed once and cached per run; if a
169
+ provider has no function calling, the system falls back to a direct structured
170
+ analysis path. The growing tool conversation is capped at three rounds, each
171
+ response is capped where the provider supports output limits, and the shared
172
+ prefix uses short-lived prompt caching.
173
+
174
+ ### Synthesize
175
+
176
+ Primary: [`src/phases/synthesize.ts`](./src/phases/synthesize.ts). Three paths:
177
+
178
+ - **Deterministic zero-call** — high-confidence Fast extractions.
179
+ - **Single-pass** — when the compacted conversation fits under the configured threshold.
180
+ - **Hierarchical** — for larger sessions: merge available boundaries → split oversized semantic chunks → batch by token budget → summarize batches → assemble.
181
+
182
+ Behaviors: session-aware prompting, decision propagation across later batches,
183
+ mode-specific single-pass thresholds and output limits, provider-aware
184
+ concurrency (wave scheduling), aggregate prompt-token reservation, and a
185
+ deterministic fallback assembly when any budget or LLM call fails.
186
+
187
+ ### Verify
188
+
189
+ Primary: [`src/phases/verify.ts`](./src/phases/verify.ts). Scores the summary
190
+ against the deterministic extraction. It checks for missing modified files,
191
+ missing unresolved errors, missing high-confidence constraints, weak goal
192
+ coverage, missing structure sections, suspicious fabricated file references,
193
+ done/unresolved inconsistencies, missing explicit decisions, and missing
194
+ open-loop coverage.
195
+
196
+ **Repair order is intentional:** (1) deterministic patch first (free,
197
+ idempotent) → (2) one LLM patch only in `thorough` mode if still insufficient
198
+ → (3) replace lower-scoring output with the deterministic quality floor → (4)
199
+ reject unless final verification has no gaps and meets the verified threshold.
200
+ Final verification runs again after continuity injection. The final scalar is
201
+ reported as repaired **verification coverage**, alongside the pre-repair source
202
+ score and fallback provenance; it is not labeled as raw synthesis quality.
203
+ Polarity checks are symmetric: adding negation to a positive fact is rejected
204
+ just as removing negation from a prohibition is. Unresolved-error source
205
+ snippets and fallback-rendered evidence share `summaryEvidenceLine()`, so
206
+ Markdown prefixes and multiline wrapping cannot create false missing-error gaps.
207
+
208
+ ## EESV hardening and control surfaces
209
+
210
+ - **Canonical summary IR** accepts recognized H1/H2/H3 headings, preserves Progress subsections, and merges duplicate canonical kinds before state mutation.
211
+ - **Typed verification gaps** drive mandatory deterministic repair; collision-aware path needles prevent basename cross-satisfaction. Provenance is persisted and shown before optional approval.
212
+ - **Fine tool semantics** separate read/search/list/mutate/delete/execute operations. Pruning deduplicates only identical idempotent access signatures.
213
+ - **Unified token planning** uses a run-bound estimator with bounded process-shared provider/model calibration, counts structured tool-call arguments, preserves an adaptive recent tail, targets mode-specific post-compaction headroom, reserves 25% of the synthesis allowance for deterministic post-summary state sections, and reserves/reconciles every request against the mode's aggregate prompt/output-token caps.
214
+ - **Security boundaries** scrub high-confidence secrets before provider calls and durable cache/backup/state writes; PII scrubbing is opt-in.
215
+ - **Policy controls** include focus weighting, exact call/latency budgets, fail-closed manual approval, online damage monitoring, and persisted open-loop overrides.
216
+ - **Release gate** (`bun run gate`) covers adversarial parser, verification, tool, cache, budget, scrub and damage fixtures.
217
+
218
+ ## State, caching & persistence
219
+
220
+ Post-verification, `app/steps/state.ts` + `src/utils/state.ts` enrich the
221
+ summary, then `domain/yield-gate.ts` measures the final replacement. Planning
222
+ has already reserved the bounded post-synthesis enrichment band by reducing the
223
+ retained tail; missing the original target or 10% net-saving floor still throws
224
+ before a `StatedRc` can reach staging/apply. `session_before_compact` only stages a passing candidate; after
225
+ the host emits the matching `session_compact`, `app/steps/persist.ts` commits
226
+ reusable state and success telemetry. Aborted/unconfirmed candidates write
227
+ neither, and the UI reports `Applied` only after that correlated commit.
228
+ `ui/error-format.ts` converts verification/yield failures to one bounded,
229
+ content-free diagnostic and collapses unknown multiline errors; full stacks are
230
+ suppressed by default and emitted only under explicit `DEBUG=smart-compact`.
231
+ Manual execution uses a two-line widget: a colored EESV phase chain plus a
232
+ phase-specific action brief. Before Apply it explicitly says the conversation
233
+ is unchanged. Routine info toasts are hidden unless `verbose`; handled provider,
234
+ watchdog, Explore, batch, and assembly failures switch to deterministic fallback
235
+ without printing raw messages. Auto-trigger rejection logs are also debug-only,
236
+ leaving one content-free safe-fallback notice in the UI.
237
+
238
+ | Concern | Where | Notes |
239
+ | --- | --- | --- |
240
+ | Open-loop injection | `utils/state.ts` | inserted before Next Steps via the canonical parser |
241
+ | `CompactionState` | `utils/state.ts` | conservatively merged and bounded across goal wording changes; newer file evidence resolves delete/present contradictions |
242
+ | Continuity Ledger | `utils/state.ts` | prior facts carry forward until positive resolution evidence or an explicit override; goal shifts become non-destructive breadcrumbs |
243
+ | Cross-compaction delta | `utils/state.ts` | "Changes Since Last Compaction" section |
244
+ | Incremental extraction cache | `utils/cache.ts` + `utils/id-fingerprint.ts` | bounded SHA-256 prefix fingerprint + tail; safe only when the pruned prefix still matches |
245
+ | Synthesis cache | `infra/synthesis-cache.ts` | behavior key includes normalized focus, route, mode, budgets, and reasoning |
246
+ | Session-log recovery | `utils/session-log.ts` | streaming JSONL parse; bypasses pi-toolkit truncation by entry-id mapping |
247
+ | Project fingerprint | `utils/fingerprint.ts` | locked read/merge/write; language/framework/key dirs stay bounded across sessions |
248
+ | Damage detection | `utils/damage.ts` | best-effort post-compaction regression signals |
249
+ | Context graph | `infra/context-graph.ts` | SQLite FTS5 facts + file edges; 2,000 non-structural nodes per project |
250
+
251
+ Apply-confirmed verified state is queued and duplicate updates coalesce only
252
+ for the exact project/session/branch head, then indexed on the next event-loop
253
+ turn so SQLite work is not part of the native
254
+ compaction hook's latency. `infra/context-graph.ts` adapts the same synchronous
255
+ query/transaction contract to `bun:sqlite` in Bun tests and `node:sqlite`
256
+ `DatabaseSync` in Pi's Node runtime; the packed release audit exercises both.
257
+ The referenced queue timer survives extension
258
+ shutdown/reload in the process; graph data is derived and a later cumulative
259
+ state safely supersedes a missed update after a hard process kill. Fact occurrences are branch-head scoped; state, recall, and resolution use the
260
+ complete host-visible branch ancestry before equivalent facts are deduplicated.
261
+ Schema v1 preserves user-confirmed manual memory but resets older derived
262
+ compaction nodes once so sibling branches cannot inherit a last-writer identity.
263
+ Recall starts from FTS5 lexical matches, expands one hop
264
+ through file-reference edges, then applies session, branch, fact-kind,
265
+ confidence, recency, and explicit-memory weights. Exact equivalent facts are
266
+ deduplicated before bounded output. Resolved/superseded state is removed from
267
+ the active FTS index; another project's rows are never eligible.
268
+
269
+ **Important retention limits:** pending in-memory compaction 5 min · exploration
270
+ tool-support cache 1 h / 128 routes · token calibration 128 routes · extraction
271
+ cache 1 h · compaction state 7 d · context graph 2,000 non-structural fact nodes
272
+ and 500 active manual memories per project · remediation hints 7 d · metrics and damage
273
+ JSONL logs 5 MiB each.
274
+
275
+ ## Concurrency & safety model
276
+
277
+ The extension is built to run safely alongside other Pi sessions and other
278
+ extensions.
279
+
280
+ ### Pending-compaction slot
281
+
282
+ [`src/app/pending-slot.ts`](./src/app/pending-slot.ts) is an encapsulated,
283
+ host-agnostic state cell (one producer, one consumer, single-threaded event
284
+ loop). `consume()` returns a discriminated result:
285
+
286
+ | `ConsumeResult.kind` | Meaning |
287
+ | --- | --- |
288
+ | `ok` | fresh payload for this session |
289
+ | `empty` | nothing staged |
290
+ | `expired` | older than the 5-minute TTL |
291
+ | `mismatch` | staged by a **different** session (cross-session leak guard) |
292
+
293
+ Session identity comes from [`infra/session-identity.ts`](./src/infra/session-identity.ts):
294
+ a real id when the host exposes one, otherwise a per-call unforgeable
295
+ `unresolved:<uuid>` — two unresolved sessions can never collide.
296
+
297
+ ### Cancellation deadlines
298
+
299
+ Some providers ignore `AbortSignal`. The auto-trigger therefore uses a shared
300
+ [`ExternalCancellation`](./src/app/run-smart-compact.ts) handle as a second line
301
+ of defense: an outer `setTimeout` fires `abort()` and sets `timedOut`, and every
302
+ side-effect gate in the orchestrator checks that flag before writing state or
303
+ applying compaction. The caller waits for safe pipeline unwind; no unsafe
304
+ `Promise.race` hard return can leave work running past the hook lifecycle.
305
+
306
+ ### Filesystem & concurrency
307
+
308
+ JSON/text cache writes use [`src/infra/fs.ts`](./src/infra/fs.ts): private
309
+ artifact directories are 0700 and files are 0600; atomic temp-file + rename
310
+ prevents half-truncated readers but intentionally does not claim fsync/power-loss
311
+ durability. Append/trim operations use a `mkdir`-based cross-process lock and
312
+ fail closed if ownership cannot be acquired, so sessions cannot interleave
313
+ bytes. SQLite supplies its own WAL durability. Native continuity handoffs are
314
+ one-shot, bounded, and keyed by project + session + branch head.
315
+
316
+ ## Provider awareness
317
+
318
+ [`src/utils/tokens.ts`](./src/utils/tokens.ts) keeps a per-provider capability
319
+ table (Anthropic, OpenAI, Google, DeepSeek, MiniMax, Xiaomi, Mistral, xAI, …)
320
+ with unknowns falling back to a safe default + fuzzy alias matching. Each entry
321
+ drives pipeline behavior:
322
+
323
+ | Capability | Drives |
324
+ | --- | --- |
325
+ | `maxOutputTokens` | caps synthesis / patch budgets |
326
+ | `supportsTools` (`true \| false \| "probe"`) | exploration tool-call probing |
327
+ | `concurrencyLimit` | batch synthesis wave scheduling |
328
+ | `cacheStrategy` | prompt-cache retention per call |
329
+ | `timeoutMultiplier` | auto-trigger hard-timeout headroom |
330
+ | `singlePassTokenMultiplier` | single-pass vs chunked threshold |
331
+ | `tokenRatioEstimate` | token estimation; refined by per-(provider,model) **EMA calibration** |
332
+
333
+ The Codex limiter is hybrid. Custom Codex endpoints receive
334
+ `max_output_tokens` through Pi AI's payload hook. The ChatGPT subscription
335
+ endpoint rejects every wire output-cap field, so it uses a derived 15–90s
336
+ per-call stream watchdog plus a visible-output ceiling; aborts route to the
337
+ phase's deterministic fallback.
338
+
339
+ ### Provider evaluation and routing evidence
340
+
341
+ [`src/domain/provider-evaluation.ts`](./src/domain/provider-evaluation.ts)
342
+ collapses call telemetry into Explore/Synthesize/Verify routes and compares
343
+ provider/models across a deterministic context-pressure × tool-density matrix.
344
+ Only an explicitly attributed pre-repair synthesis score contributes route
345
+ quality; a run's final verifier score is never copied into Explore/Verify.
346
+ Legacy or operational-only routes still contribute latency and reliability.
347
+ Recommendations require minimum samples, ≥80% call reliability, and ≥50%
348
+ stage-local quality coverage, shrink toward neutral under low confidence, and
349
+ are advisory only.
350
+ They never mutate config or replace the selected model. The opt-in live harness
351
+ runs three identical bounded continuity scenarios across explicitly named
352
+ models.
353
+
354
+ ### Privacy-safe telemetry and canary decisions
355
+
356
+ [`src/domain/telemetry.ts`](./src/domain/telemetry.ts) maps raw exceptions to a
357
+ content-free failure taxonomy, aggregates schema-v2 run quality without IDs or
358
+ conversation data, and compares an explicitly tagged `canary` cohort against
359
+ `stable` history. Reports expose total/applied counts; only non-dry,
360
+ host-confirmed applied outcomes count toward promotion. A deterministic green
361
+ release check is not promotion evidence. The gate returns Hold, Rollback, or
362
+ Promote from applied sample/quality coverage plus failure, verifier quality, p95
363
+ latency, token, heuristic-fallback, and post-compaction-damage thresholds.
364
+ Damage observations join their originating compaction by local run id, dedupe
365
+ per run, and require ≥70% stable/canary coverage before promotion. It is
366
+ advisory: rollout selection, configuration changes, and rollback remain external.
367
+
368
+ Dashboard trust calculations live in
369
+ [`src/ui/dashboard-insights.ts`](./src/ui/dashboard-insights.ts). Data
370
+ Confidence is an auditable 100-point score over sample size, schema-v2
371
+ coverage, verifier-quality coverage, required-field completeness, and
372
+ freshness; ≥85 is the high-confidence target. TUI and HTML surfaces share the
373
+ same quality-repair, stage/provider/model, failure-taxonomy, and
374
+ stable-vs-canary aggregates. Missing/legacy evidence lowers the score and
375
+ produces remediation guidance instead of being imputed.
376
+
377
+ ## Dependency injection
378
+
379
+ [`src/infra/services.ts`](./src/infra/services.ts) is a per-`runSmartCompact`
380
+ service bag. Metrics, budgets, scrubbers, and prompt namespaces are isolated per
381
+ run. Production shares only bounded provider/model capability and calibration
382
+ knowledge, which contains no conversation/session data; tests use isolated
383
+ stores by default:
384
+
385
+ | Service | Role |
386
+ | --- | --- |
387
+ | `clock` | injectable wall clock (deterministic tests) |
388
+ | `llm` | LLM client seam (production does not replay failed requests) |
389
+ | `toolSupport` | process-shared in production; explicit unsupported capability, 1 h TTL / 128 routes |
390
+ | `metrics` | bounded metrics sink |
391
+ | `extractionCacheStats` | hit / miss counters |
392
+ | `tokenCalibration` | process-shared bounded per-(provider,model) EMA factors |
393
+ | `compactSessionId` | per-run prompt-cache namespace |
394
+
395
+ ## Layer responsibilities
396
+
397
+ The code is organized into six layers, each with a single responsibility.
398
+
399
+ ### Entry layer
400
+
401
+ | File | Responsibility |
402
+ | --- | --- |
403
+ | `src/index.ts` | extension registration, command parsing, auto-trigger hook |
404
+ | `src/constants.ts` | version, thresholds, prompts, config keys |
405
+ | `src/types.ts` | shared types and discriminated unions |
406
+ | `domain/provider-evaluation.ts` | advisory provider scenario matrix and route telemetry aggregation |
407
+ | `domain/telemetry.ts` | privacy-safe aggregates, failure taxonomy, and canary rollback gates |
408
+
409
+ ### Orchestration layer (`src/app/`)
410
+
411
+ | File | Responsibility |
412
+ | --- | --- |
413
+ | `app/run-smart-compact.ts` | top-level pipeline orchestrator |
414
+ | `app/run-context.ts` | typed stage chain (`RcBase → … → StatedRc`) |
415
+ | `app/mode-policy.ts` | Auto selector and finite Fast/Balanced/Thorough policies; legacy Aggressive maps to Fast |
416
+ | `app/pending-slot.ts` | encapsulated pending-compaction state cell |
417
+ | `app/steps/prepare.ts` | resolve config, auth, provider caps |
418
+ | `app/steps/window.ts` | pick the prefix of messages to compact |
419
+ | `app/steps/recover.ts` | recover full content for log-truncated messages |
420
+ | `app/steps/tier.ts` | choose compaction tier (none / light / full) |
421
+ | `app/steps/extract.ts` | pruning + deterministic extraction with incremental cache |
422
+ | `app/steps/synthesize.ts` | single-pass / EESV synthesis |
423
+ | `app/steps/verify.ts` | structural verification + repair |
424
+ | `app/steps/state.ts` | enrich summary with state machine + open loops |
425
+ | `app/steps/persist.ts` | apply compaction, save fingerprint, persist state |
426
+ | `app/steps/metrics.ts` | record success / failure metrics |
427
+
428
+ ### Domain layer (`src/domain/`)
429
+
430
+ Pure semantics — no I/O, no async, no globals.
431
+
432
+ | File | Responsibility |
433
+ | --- | --- |
434
+ | `domain/summary-schema.ts` | canonical section kinds + heading classification |
435
+ | `domain/summary-parse.ts` | parse/render canonical H1/H2/H3 sections; merge duplicates; placement (`before`/`after`) |
436
+ | `domain/tool-semantics.ts` | fine tool operation taxonomy with broad compatibility wrapper |
437
+ | `domain/scrub.ts` | pure secret/PII redaction primitives and run-scoped scrubber |
438
+
439
+ ### Algorithm layer (`src/phases/`)
440
+
441
+ | File | Responsibility |
442
+ | --- | --- |
443
+ | `phases/explore.ts` | targeted exploration with tool-call probing |
444
+ | `phases/synthesize.ts` | chunking, single-pass compact, batch summarization, assembly |
445
+ | `phases/verify.ts` | typed gap detection, collision-safe coverage, deterministic/LLM repair |
446
+
447
+ ### Infrastructure layer (`src/infra/`)
448
+
449
+ All external-world interaction.
450
+
451
+ | File | Responsibility |
452
+ | --- | --- |
453
+ | `infra/fs.ts` | atomic writes, advisory locks |
454
+ | `infra/paths.ts` | canonical cache/session/backup paths |
455
+ | `infra/git.ts` | cached git-root discovery |
456
+ | `infra/clock.ts` | injectable wall clock |
457
+ | `infra/llm-client.ts` | LLM seam, custom-Codex wire cap, and ChatGPT Codex stream watchdog |
458
+ | `infra/services.ts` | per-run services container |
459
+ | `infra/session-identity.ts` | robust session-id resolution with opaque `unresolved:` fallback |
460
+ | `infra/ai-messages.ts` | boundary adapters between `LlmMessage` and pi-ai `Message` |
461
+
462
+ ### Utility layer (`src/utils/`)
463
+
464
+ | File | Responsibility |
465
+ | --- | --- |
466
+ | `utils/extraction.ts` | deterministic fact extraction (files, errors, decisions) |
467
+ | `utils/pruning.ts` | redundancy removal on the message list |
468
+ | `utils/state.ts` | structured state, open loops, delta, pinned-path preservation |
469
+ | `utils/helpers.ts` | config, backups, batching, shared helpers, backup list/restore |
470
+ | `utils/cache.ts` | metrics log + extraction prefix cache |
471
+ | `utils/fingerprint.ts` | project fingerprinting (language, framework, deps) |
472
+ | `utils/damage.ts` | post-compaction regression signals + remediation hints |
473
+ | `utils/id-fingerprint.ts` | compact SHA-256 fingerprint of entry-id arrays |
474
+ | `utils/file-needles.ts` | path-suffix needles for error→file attribution |
475
+ | `utils/file-ref-detect.ts` | fabricated file-reference detection (SemVer-rejecting) |
476
+ | `utils/session-log.ts` | streaming JSONL parser for the Pi session log |
477
+ | `utils/tokens.ts` | per-(provider,model) token estimation with EMA calibration |
478
+ | `utils/type-guards.ts` | runtime validators for cross-version compatibility |
479
+ | `utils/logger.ts` | stderr-prefixed log shim |
480
+ | `utils/lru.ts` | small bounded LRU cache primitive |
481
+
482
+ ### UI layer (`src/ui/`)
483
+
484
+ | File | Responsibility |
485
+ | --- | --- |
486
+ | `ui/overlays.ts` | progressive preflight, semantic phase progress, approval review, and dashboard screens |
487
+ | `ui/dashboard-format.ts` | shared pure formatters for metrics surfaces |
488
+ | `ui/dashboard-insights.ts` | Data Confidence, quality/provider drilldowns, and canary trust views |
489
+ | `ui/metrics-report.ts` | text report + local HTML metrics dashboard |
490
+
491
+ ## Design principles
492
+
493
+ The architecture intentionally biases toward safety:
494
+
495
+ - deterministic extraction before any synthesis
496
+ - adaptive exploration instead of always-on tool use
497
+ - verified file lists and error context
498
+ - deterministic repair before additional LLM calls
499
+ - hallucinated file-reference detection
500
+ - stateful tracking of open loops and cross-compaction deltas
501
+ - tool-driven compaction never compacts mid-turn
502
+ - summaries preserve exact file paths and identifiers where possible
503
+ - the recent tail stays live outside the compacted region
504
+
505
+ ## Extending the system
506
+
507
+ When adding features, prefer this order:
508
+
509
+ 1. extract more deterministic signal if possible
510
+ 2. enrich exploration only when needed
511
+ 3. keep synthesis prompts structured and bounded
512
+ 4. strengthen verification before increasing model dependence
513
+ 5. update tests and docs in the same change
package/CHANGELOG.md CHANGED
@@ -4,6 +4,31 @@
4
4
 
5
5
  No changes yet.
6
6
 
7
+ ## [8.0.8] - 2026-08-09
8
+
9
+ ### Fixed
10
+
11
+ - Modified-file verification now accepts exact normalized paths from canonical `Files Modified` entries before collision-safe suffix matching. Root files that share a basename with nested files, plus top-level generic or short filenames, can be deterministically repaired without weakening the zero-gap verification gate.
12
+
13
+ ## [8.0.7] - 2026-08-08
14
+
15
+ ### Fixed
16
+
17
+ - Window planning now reserves 25% of the LLM summary allowance for deterministic verification, delta, open-loop, and continuity sections added after synthesis. The reported `29,355t` versus `28,008t` near-target failure now plans a smaller retained tail and lands below the original hard target; the exact post-summary target gate and 10% minimum-saving floor remain fail-closed.
18
+ - Auto risk refinement changes analysis depth without mutating the already-planned profile/output allowance, preventing a late Fast/Balanced/Thorough profile switch from invalidating the target contract. Manual preflight now scans and tokenizes the active branch once for all three mode previews.
19
+ - Continuity now uses the latest substantive user request, parses host-compacted `Goal` sections, ignores acknowledgement-only turns, and treats free-form goal changes as non-destructive breadcrumbs. Prior errors, loops, next actions, and critical context remain active until positive resolution evidence or an explicit override; LLM goal paraphrases cannot silently resolve them.
20
+ - Known transient provider/invocation diagnostics no longer become durable blockers, while project test failures that mention HTTP 429 remain real errors. Initial and merged open-loop state is capped with active, high-priority work ahead of resolved history.
21
+ - File status follows newer successful access/mutation/delete evidence. Existing paths are removed from legacy `deletedFiles` state before persistence, eliminating the three false deletions observed in the v8.0.6 production run.
22
+ - Context-graph facts now use branch-head occurrences and lineage-scoped resolution, preventing equivalent sibling-branch facts from overwriting or closing each other. Schema v1 preserves manual memories while resetting older derived compaction nodes once.
23
+ - Release audit accepts both legacy-array and npm 12 object-shaped `npm pack --json` output, and the unsupported source-only Git install instruction was removed.
24
+ - Result and approval UX now labels `100/100` as post-repair verification coverage, shows the raw source score, and explicitly identifies deterministic safety-fallback runs instead of presenting repaired coverage as raw synthesis quality.
25
+ - Verification now rejects polarity changes symmetrically, and bounded absence never resolves continuity facts.
26
+ - Backups contain the complete selected pre-prune conversation after scrubbing; private artifact directories/files enforce `0700`/`0600`.
27
+ - Project memory fails closed when cwd is exactly HOME or the filesystem root, displays the complete scrubbed value for host confirmation, and caps active manual facts at 500 per project.
28
+ - Continuity and context-graph resolution use full visible branch ancestry; focus participates in synthesis cache identity, and project fingerprints use bounded locked updates.
29
+ - Canary reports total/applied runs and requires non-dry applied telemetry; deterministic green checks never imply `PROMOTE`.
30
+ - Auto timeout remains a cancellation deadline that waits for safe pipeline unwind, and yield failures use the canonical `yield` telemetry kind.
31
+
7
32
  ## [8.0.6] - 2026-08-07
8
33
 
9
34
  ### Changed