fix(linkedin-studio): N1 — prune-regex no-op + dato-uavhengige kalender-fixtures (TDD) [skip-docs]

B-F1 (prod-bug, prune de facto no-op): fjernet `m`-flagget fra Recent Posts-
regexen i `state-updater.mjs`. Med `/m` matchet `$`-alternativet i lookahead
slutten av hver linje, så den late capturen stoppet etter FØRSTE entry-linje;
gamle entries under en fersk (nye prepender øverst) ble aldri skannet/prunet.
Ny regresjonstest (old-under-fresh) rød->grønn beviser mekanismen.

B-F2 (kalender-flake): `pruneContentHistory` fikk valgfri `today = new Date()`-
param (deterministisk rotårsak-fix; CLI/hook-kall uendret via default). Prune-
testene injiserer fast syntetisk today og asserter kun på Recent Posts-seksjonen
(ikke helinnhold/frontmatter), så today-100d aldri kolliderer med SAMPLE_STATE-
datoer. Samme grep i `scripts/check-replace-safety.mjs`.

Suiter (alle grønne): test-runner 138/0 · hooks 140/0 (+1 regresjonstest) ·
trends 245/0 · brain 134/0 · tests 35/0 · render 20/0.

Utført av Fable 5 (high) subagent, orkestrert + verifisert av Opus-hovedkontekst.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: a814039f-da8b-41b1-8d7c-60abe1e03c5a
This commit is contained in:
Kjell Tore Guttormsen 2026-07-16 20:03:05 +02:00
commit 776d728d7d
3 changed files with 91 additions and 51 deletions

View file

@ -133,17 +133,21 @@ export function updatePostTracking(stateContent, { postDate, postTopic, hookText
* Remove Recent Posts entries older than maxAgeDays.
* @param {string} stateContent - Full state file content
* @param {number} [maxAgeDays=90]
* @param {Date} [today=new Date()] - Injectable clock (tests pass a fixed date)
* @returns {{ content: string, pruned: number } | null}
*/
export function pruneContentHistory(stateContent, maxAgeDays = 90) {
const today = new Date();
export function pruneContentHistory(stateContent, maxAgeDays = 90, today = new Date()) {
const cutoff = new Date(today);
cutoff.setDate(cutoff.getDate() - maxAgeDays);
const cutoffStr = cutoff.toISOString().slice(0, 10);
// Find all Recent Posts entries
const entryPattern = /^- \[(\d{4}-\d{2}-\d{2})\] .+$/gm;
const recentSection = stateContent.match(/## Recent Posts\n\n?([\s\S]*?)(?=\n## [^R]|\n## $|$)/m);
// No /m flag: with /m the $ alternative in the lookahead matched the end of
// EVERY line, so the lazy capture stopped after the FIRST entry line and
// older entries below it were never scanned (and never pruned). Without /m,
// $ matches only end-of-string and the capture spans the whole section.
const recentSection = stateContent.match(/## Recent Posts\n\n?([\s\S]*?)(?=\n## [^R]|\n## $|$)/);
if (!recentSection || !recentSection[1].trim()) return null;
const sectionContent = recentSection[1];