# Release 0.4.2 — Class-only diagnostics for rejected exact-value spans

**Coordinated packages:** `ds4-context-core`, `ds4-context-reference-adapter`, and `ds4-context-engine` 0.4.2.
**Implementation commit:** `6ecd2af`.

## Summary

Diagnostics patch. Fail-closed exact-value validation now produces a class-only
report of the rejected backticked spans, and the existing
`compaction.custom_fallback` warning carries it as `unsupportedSpanClasses`.
Validation strictness, the eight-bullet and 25% repair bounds, the fail-closed
fallback to Pi default compaction, and every provider-facing default are
unchanged. The report contains class names and counters only: it never contains
span text, so it is safe to log and to quote in reports.

## Changes

- `packages/core/src/compaction/summary-contract.ts` adds
  `classifyUnsupportedExactValueSpans()`, a pure observation-only classifier
  returning an `UnsupportedSpanClassReport`:
  - `relations` counts near-miss relations between the rejected span and the
    segment source: `escaped-form-present`, `unescaped-form-present`,
    `whitespace-collapsed-present`, `case-variant-present`,
    `typographic-variant-present`, `single-deletion-present`,
    `composed-two-present-parts`, `no-near-miss`, `not-classified-budget`.
  - `shapes` counts structural shapes: `space`, `backslash`, `double-backslash`,
    `colon`, `equals`, `double-dash`, `slash`, `quote`, `typographic-char`,
    `digit-group-separator`, `json-punctuation`.
  - `lengthBuckets` counts span lengths: `len-1-8`, `len-9-16`, `len-17-32`,
    `len-33-64`, `len-65-120`, `len-121-plus`.
  - `spans`, optional `bullets`, and `classificationComplete` report the totals
    and whether the evidence-probe budget stopped the near-miss classification.
  - The classifier is bounded: probe budget 4000 evidence lookups, near-miss
    spans limited to 96 characters, composed parts required to be at least four
    characters. It cannot alter validation or repair.
- `src/pi-adapter/summary-generator.ts`: `SummaryValidationError` carries the
  report and machine-readable issue codes. Error message and behavior are
  unchanged.
- `src/pi-adapter/compaction-coordinator.ts`: the existing
  `custom_fallback` warning gains `unsupportedSpanClasses` when a report exists.
- Tests: core classifier coverage, generator wiring, coordinator logging, and a
  guard asserting that logged diagnostics contain no span text.
- `docs/COMPACTION.md`: diagnostics and validation sections describe the report
  and its limits.

## Why

Production sessions on separate hosts failed closed with
`compaction.custom_fallback`, `unsupported-exact-value`,
`repair=too-many-bullets`, and observed counts of 30/20, 23/19, 19/15, and
17/11 unsupported spans and affected bullets. The bounded repair removes at most
eight whole bullets and refuses without attempting any removal above that, so
every one of those runs fell back to Pi default compaction unmodified.

Counts alone cannot attribute the failure: a summarizer that invents values and
a summarizer whose rendering or composition rules differ from the validation
domain need opposite fixes, and the two are indistinguishable in a bare
`unsupportedSpans` number. 0.4.1 changed the prompt for the composition
hypothesis; the following observed runs kept the same failure class and the same
order of magnitude, so the next step is attribution rather than another prompt
edit. This release adds only the measurement.

## Privacy and safety

- No conversation text is added to any log, warning detail, or error message.
  The report is a fixed taxonomy of class names plus integer counters.
- The classifier performs bounded lookups against the already-sanitized segment
  source used by validation; it does not read session files, SQLite, or any
  other data, and it does not run on successful summaries.
- Privacy enforcement, provider-facing payloads, and `compaction` phase timings
  are untouched.

## Compatibility and persistence

- No SQLite migration or schema change (still 16), no canonical Pi JSONL change,
  no privacy consent change, no portable runtime-adapter contract change.
- Validation strictness, the eight-bullet bound, the 25% removal bound, and the
  fail-open behavior of the extension remain exactly as in 0.4.1.
- Provider-facing defaults are unchanged: `chars-v1` remains the default
  estimator, `modelAwareness.autoTune` and DS4 native continuation stay
  disabled, `storage.scope` keeps its `project` default.

## Measured scope and limitations

The classifier is diagnostic: it does not fix the observed fallback and no
reduction of the fallback rate is claimed. Its output is only as good as the
rejected spans it is given, and `classificationComplete: false` marks the runs
where the probe budget stopped the near-miss classification.

## Related operational finding (not a change in this release)

On the same host an independent failure mode was identified outside DS4: Pi's
own summarization request caps output at
`Math.min(Math.floor(0.8 * reserveTokens), model.maxTokens)` with
`reserveTokens` defaulting to 16384, i.e. 13107 output tokens for the history
summary and 8192 for the turn-prefix summary, and Pi passes the session thinking
level to that request. Both the dedicated-model attempt and the fallback attempt
reported the same stop condition
(`Summarization failed: generation hit the token cap and the summary is
incomplete`, raised for `stopReason === "length"`), so compaction ended in an
error even after DS4 and the dedicated compaction model had both declined.
Raising `compaction.reserveTokens` globally, or per model through
`compaction.modelOverrides`, is an operator setting in Pi and is not part of
this release.

## Validation

On Node 26.5.1 the release passed `npm run check` (builds, `tsc --noEmit`, 99
Vitest files and 623 tests), deterministic `npm run quality:compare` (candidate
`0.9875` against baseline `0.808156`), `npm run schema:context-persistence`
(`passed: true`), and `npm run pack:check` in a clean consumer for all three
packages at 0.4.2 (247 core files, 7 reference-adapter files, 100 extension
files). The coordinated publication then proceeded in dependency order
(`ds4-context-core`, then `ds4-context-reference-adapter`, then
`ds4-context-engine`), each command reporting the exact 0.4.2 artifact,
followed by `npm run registry:check -- 0.4.2`. Registry propagation lagged the
publishes: the first `npm run registry:check -- 0.4.2` failed with
`ETARGET ... No matching version found for ds4-context-core@0.4.2`, and six
retries over roughly four minutes reported the same error while the registry
metadata already listed 0.4.2. Once propagation completed, the repeated
`npm run registry:check -- 0.4.2` reported "Verified all DS4 registry packages
at exact version 0.4.2", and the `latest` dist-tag resolved to 0.4.2 for all
three packages. No provider calls are involved in this release procedure.
