Skip to content

war-memory tighten hardening — #989 date validation + #992 target-aware verdict (v0.14.54) - #1061

Merged
Ljferrer merged 13 commits into
masterfrom
dev/2026-07-22-war-memory-hardening
Jul 24, 2026
Merged

war-memory tighten hardening — #989 date validation + #992 target-aware verdict (v0.14.54)#1061
Ljferrer merged 13 commits into
masterfrom
dev/2026-07-22-war-memory-hardening

Conversation

@Ljferrer

Copy link
Copy Markdown
Owner

Campaign plan 6 of 92026-07-22-run-resilience-and-hardening. Stacked (ADR 0011) on dev/2026-07-22-test-floor-target-repo (plan 5, PR #1052). Merge bottom-up: this PR merges after #1052.

Closes the two code-verified holes in the /lessons-learned tighten eviction ladder.

What changed

#989effectiveDate now validates the date tokens it scrapes from lesson prose. A date-shaped match is kept only if it (1) round-trips through a UTC parse — an invalid stamp like 2026-13-45 is dropped — and (2) lands no more than 48 h (FUTURE_SKEW_MS) past the current clock — a stray far-future token like 2099-01-01 no longer counts. Fail-safe direction preserved: when no token survives, the lesson reads undated and stays protected, exactly as before. Validation is additive via an { now } options arg; the lessonRecord call site is unchanged.

#992tighten-plan's verdict is now the stricter of the advisory line and the effective --target. A sub-advisory --target makes the preflight bind there instead of silently deferring to the fixed 17,000 B advisory. buildProjection is byte-untouched — only tightenPlan's returned verdict is overloaded; the default path is provably byte-identical.

Doc-retire (default-flip discipline): the ratified fixed-advisory prose is retired across six enumerated SKILL.md surfaces + two CONTEXT.md glossary entries, each locked OLD-absent (not merely NEW-present) in lessons-learned-doc-contract.test.mjs, which now also loads CONTEXT.md. Both origin lessons amended in place with dated MITIGATED (#989) / RESOLVED (#992) notes.

Phases

phase tip result
1 — hardening + doc retire + lesson amendments 87f42ef 3/3 tasks, 12/12 End states, 0 escalations
2 — Release (v0.14.54) c74a1fc 1/1, End state 13 met

Gate at land: 891/891 JS + 26/26 shell, war-memory lint clean, version-slots.test.mjs 3/3.

Follow-up filed

Unexecuted backstops (AI-declared, ADR 0014)

check why deferred runner
End-to-end #992 fix on the live corpus + interactive strike-list gate needs a live operator, a live corpus between the target and 17,000 B, and the interactive gate; CI covers the verdict contract on fixtures operator, at the first custom-target tighten post-release
FUTURE_SKEW_MS (48 h) vs real UTC-midnight-vs-local-date skew needs a real local-timezone wall clock at a date boundary; tests inject now operator observation across routine tighten-plan/render runs

🤖 Generated with Claude Code

Ljferrer and others added 13 commits July 23, 2026 03:52
Round 1 found two root defects: the plan froze the Preflight stop instruction
VERBATIM while that very region carried two claims the change falsifies, and
the Purpose (a custom --target governs whether the preflight triggers) was
unreachable because the live invocation passes no --target.

Rounds 2-3 caught defects the patches themselves introduced - a
self-contradictory surface enumeration with dangling labels, a
non-discriminating grep that already passed, an orphaned fence lock no task
claimed, a needle quoting a phrase that wraps a line break, and a false
"these surfaces carry no crisp retired token" claim that would have left the
default-flip failure mode open.

Round 3 also found cross-plan collision no earlier round saw: lessons-learned-seed
edits all three Task 1.2 files and is NOT campaign-serialized. Round 4 added the
in-campaign CONTEXT.md contention (5 siblings) my own round-1 reversal created.

9 adjudication rows for args.adjudications.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… verdict (#1056)

Closes the two code-verified holes in the `tighten` eviction ladder.

#989 — `effectiveDate` took the lexicographic max over every date-SHAPED prose
token, so a stray invalid (`2026-13-45`) or future (`2099-01-01`) stamp won and
protected a lesson from eviction forever. Validation now lives inside
`effectiveDate` itself (D1 — root cause, not the Floor-3 symptom): a token
survives only if it round-trips through `Date.parse(token + 'T00:00:00Z')` (D2 —
the engine's ISO parse IS the month/day range check) and does not post-date
`now + FUTURE_SKEW_MS` (D3 — new module-private 48 h constant). Max over
survivors; none surviving => null => the existing undated-PROTECTED path, so the
fail-safe direction is preserved. Signature is additive,
`effectiveDate(sources = [], { now = new Date() } = {})` (D4); `lessonRecord`'s
call site stays sources-only (wall clock is correct at corpus-walk time).

#992 — `tightenPlan` returned `buildProjection`'s verdict verbatim, so a custom
`--target` below the advisory never governed whether the `/lessons-learned
tighten` preflight triggered. The returned `verdict` is now the stricter of the
two (D6/D7): `refuse` passes through; `warn` on the projection's own warn OR at
`currentBytes >= target`; else `ok`. A loose target never suppresses the
advisory warn. With the default `target = WARN_BYTES` this is byte-identical to
today, so the default path is untouched. `buildProjection` is BYTE-UNTOUCHED —
render-index and archive still read the pure advisory verdict.

The Floor-3 `Number.isNaN(effMs)` guard is KEPT as a defense-in-depth backstop
(D8 — `tightenPlan` is exported and takes caller-built records); its comment is
reworded from behavior-claim to backstop. Comment sweep handled every retired
token (`lexicographic max`, `Newest stamp anywhere`, `well-formed-but-invalid`)
plus the section banner's "no parseable date anywhere".

Tests are pure additions (77 lines, 0 deletions) — no existing test modified,
skipped, or weakened. The prose-only recurrence floor test, the render-verdict
trio and the cut-line `target: 2000` test all pass unmodified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…alidated contract (#1056)

Absorbs two audit Nit findings (advisory polish, comment-only):

1. [Nit] Test-file section banner still describes the pre-validation
   effectiveDate contract (war-memory.test.mjs:932) — the block banner read
   'newest ISO date ... null when nothing parses', the OLD unconditional
   contract; after the diff the rule is 'newest VALID, non-future token; null
   when none survives'. The sweep was scoped to war-memory.mjs, so this
   test-file straggler fell outside the swept scope.

2. [Nit] Comment-lag (ADR 0025 duty 3): the effectiveDate test-section banner
   still states the pre-#989 contract in a file this diff touches — 'null
   when nothing parses' is no longer the whole null condition (null is also
   returned when every token parses but is beyond now + FUTURE_SKEW_MS), so
   the banner under-described two of the three tests in its block.

Fix: extend the banner to 'newest VALID, non-future ISO date ...; null when
no token survives validation (undated => PROTECTED)'. No assertion touched;
existing tests stay byte-unmodified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tract locks (#1057)

`tightenPlan`'s printed `verdict` is now the stricter of the advisory projection
verdict and the effective `--target` (#992), so every doc sentence reading the
advisory line as the sole, fixed tighten trigger became false. Retire all eight
enumerated OLD-reading surfaces and lock each one mechanically.

SKILL.md (surfaces 1-6): the "never a `≤ target` reading" sentence → the
stricter-of reading; the step-1 `--target` parenthetical no longer says "only for
a different bound" (the flag also governs whether the pass triggers); the
frontmatter description clause → effective target; the Phase-0 bullet → the
*default* trigger; the verdict attribution → the effective read, not
`buildProjection`'s own; the stop-rule appositive → "strictly under the effective
target". The stop RULE itself (`ok` ⇒ report "nothing to tighten" and stop, no
later step runs) survives semantically unchanged — the round-1 "verbatim" mandate
is re-scoped to the contract, not the bytes.

Live-flow threading: the step-1 fenced command now carries
`${TARGET:+--target "$TARGET"}`, so an operator-supplied target actually reaches
the preflight; with none supplied the flag drops out and the default run is
byte-identical.

CONTEXT.md (surfaces 7-8): the **Advisory line** and **Tighten pass** glossary
entries no longer assert the advisory line is the sole trigger ("there is no
third threshold" / "triggered at the advisory line") — it is the *default*
trigger and exit target; a stricter `--target` becomes both for one invocation,
and a looser one never suppresses the advisory warn.

lessons-learned-doc-contract.test.mjs now loads CONTEXT.md (it read only SKILL.md
and references/migration.md) and adds 5 tests / 11 assertions: six OLD-absent one
per SKILL surface, two OLD-absent on CONTEXT (needles stop before the :1071→:1072
wrap), NEW-present on the reworded surface-1 sentence, NEW-present on the
surviving stop rule, and a FENCE-SCOPED NEW-present on the threaded `--target`
(a whole-region grep passes today via the parenthetical). All eleven
temp-break-proven red.
…EN_TARGET (#1057)

Absorbs two auditor-flagged Minor findings on the step-1 tighten preflight:

1. Step-1 \$TARGET relies on cross-invocation shell state; if it is unset the
   operator's target silently drops back to 17,000 B — the :+ expansion drops
   the flag when the variable is set in a separate Bash invocation from the
   fenced block, so the preflight binds at the default and reports "nothing to
   tighten" (the exact #992 silent failure mode). Fix: append the Phase 6/7
   same-shell clause to the step-1 preamble ("set it in the same shell
   invocation as the command below, or substitute the literal figure — each
   command block runs in its own shell").

2. Generic \$TARGET in the step-1 fence can inherit an ambient export,
   breaking the byte-identical default path — TARGET is a commonly-exported
   name (cargo/cross, Makefiles); a fresh shell inheriting it would silently
   thread --target x86_64-..., Number() -> NaN, and the printed plan comes
   back target: null instead of the 17,000 B default contract. Fix: rename to
   the namespaced \$TIGHTEN_TARGET in all three spots (preamble prose + fenced
   command). Lock (21) anchors on tighten-plan[^\n]*--target and is unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Task 1.3 (#1058): amend both origin lessons in place (never delete,
temperature-is-location; D9) now that Task 1.1's effectiveDate
validation + tightenPlan target-aware verdict landed on this
integration tip.

- effective-date-lex-max-over-all-date-shaped-prose-tokens-overprotects.md:
  description now leads with MITIGATED (#989, 2026-07-23); appended a
  dated MITIGATED note (code-verified against this task's rebased
  worktree) describing the UTC round-trip + FUTURE_SKEW_MS validation
  that closes the permanent-over-protection gap.
- tighten-plan-target-flag-does-not-lower-fixed-warn-bytes-preflight-stop.md:
  description now leads with RESOLVED (#992); appended a dated
  RESOLVED note describing tightenPlan's now target-aware verdict
  (stricter of the advisory read and --target, D7). The existing
  Status paragraph's historical quote of the retired SKILL.md
  sentence is left untouched as a provenance-dated record, with a
  short disclaimer pointing at the superseding note.

Both rewritten descriptions stay at/under their current byte length
(measured as the YAML value): #989 165B <= 181B, #992 97B <= 117B.
Metadata keys, provenance, and keywords are unchanged.
`node skills/_shared/war-memory.mjs lint docs/learnings/` exits 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ver residual (task 1.3 --ace)

Auditor absorb findings applied:

1. [Minor] #992 lesson's H1 title and two body paragraphs still assert the
   now-false claim as live; the added 'historical record' parenthetical is
   scoped to the Status paragraph only — a reader/retrieval hit that opens
   the file reads a false live claim before reaching the RESOLVED note.
   Fix: H1 now leads with 'RESOLVED —' (matching the binding exemplar's
   convention), and the 'What'/'The gotcha' bold lead-ins now carry
   '(as shipped 2026-07-21, superseded by #992 — see below)'. Slug/name/
   metadata unchanged; prose only.

2. [Nit] #989 MITIGATED note omits the deliberate 2026-02-31 rollover
   residual that the implementation's own comment records — since the
   lesson's original advice was literally 'validate month <= 12 && day <= 31'
   and the amendment is MITIGATED precisely because residuals remain,
   omitting it invites over-trusting the validation later.
   Fix: one clause added to the 'What's still true' paragraph — a
   day-overflow token such as 2026-02-31 still survives the UTC round-trip
   (rolls over to early March), a bounded residual accepted by design.

Gate: war-memory lint clean; node --test 886/886 pass.

Refs #1056

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eep left behind

The phase-close coherence sweep was skipped this phase, so two `absorb`-disposition
findings fell through to the follow-up list. Both re-confirmed at the landed tip and
fixed here rather than deferred:

- war-memory.test.mjs: two test titles still stated the pre-#989 contract while the
  section banner right above them had been updated to the validated one. Titles only —
  no assertion, fixture, or signature touched.
- #989 origin lesson: the H1 and the "What" lead-in asserted the retired behavior in
  present tense with no supersession marker, while the #992 sibling got exactly those
  markers in the same diff. Mirrors the sibling's shape; frontmatter untouched.

Gate: 891/891 JS + 26/26 shell green; war-memory lint clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… lint absent from gate artifacts

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bump all four version slots in lock-step to the next free patch above the
live integration base: plugin.json `version`, marketplace.json
`metadata.version` and `plugins[0].version`, and the README `## Status`
line (replace-in-place, no badge). All four slots read 0.14.53 at the phase
base — the global max across every ref — so 0.14.54 is the next free patch,
resolved from the four slots as they stand in the rebased tree
(version-slots.test.mjs is the arbiter; plan v-literals are non-authoritative).

Status blurb (additive/precise): `effectiveDate` now rejects invalid-shaped
and beyond-skew future date tokens (fail-safe preserved — none surviving ⇒
undated ⇒ protected); `tighten-plan`'s `verdict` is now the stricter of the
advisory line and the effective `--target`, so the preflight can bind at a
stricter `--target` while a looser one moves only the exit target, never the
trigger. `buildProjection`, `render-index`, and `archive --candidates`
semantics untouched. Prior release prose left as-is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… lesson (cross-campaign)

Servitor wrap-up recorded a 16th recurrence of the stale-session-worktree hazard,
now spanning four unrelated phase wrap-ups across multiple campaigns (D1 in-place
update, no new file). Task 2.1 (version bump) itself carried no durable gotcha.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant