feat(ms-ai-architect): insertToc + backfill-toc applier (Fase 1b-verktøy) — store-fil-TOC foldet inn i Cosmo-utfasing
1b-premisset (ikke-advisor storfiler er Cosmo-frie, trygt å TOC-e nå) er motbevist mot ground truth: 11/12 har en `## …Cosmo…`-overskrift → en TOC bygget fra `##`-overskriftene ville emittert ny Cosmo-anker (bryter «aldri ny Cosmo-innhold»), og å nøytralisere overskriften er selve persona-fjerningen (ikke en sidehandling — scope-guard). Store-fil-TOC (de 20 filene >800 linjer, advisor + ikke-advisor) folder derfor inn i Cosmo-utfasingen. Denne committen leverer kun det gjenbrukbare verktøyet; ingen ref-filer rørt. - transform.insertToc: retrofitter `## Innhold` i en eksisterende storfil rett før første ikke-fence `## `-seksjon (samme slot som composeKbFile), robust mot de 3 legacy-header-variantene (med/uten `---`), idempotent, fence-aware. - scripts/kb-update/backfill-toc.mjs: dry-run/--write applier (ren glue rundt insertToc; transform.mjs forblir write-fri — arkitektur-invariant). - docs/cosmo-removal-brief: «Sammenfall»-seksjon dokumenterer fold-inn + bruk. - TDD: +6 transform-tester. Suite 509/509 (kb-update 355 + kb-eval 154).
This commit is contained in:
parent
2240f1efdd
commit
bfc79710c8
4 changed files with 179 additions and 0 deletions
|
|
@ -139,6 +139,41 @@ export function composeKbFile(meta, body) {
|
|||
return `${header}\n${toc}\n${b}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backfill a `## Innhold` TOC into an EXISTING large reference file that lacks one,
|
||||
* placing it immediately before the first non-fenced `## ` section heading — the same
|
||||
* relative slot composeKbFile gives a freshly-generated file (TOC between the header
|
||||
* preamble and the first content section), but robust to the varied legacy headers on
|
||||
* disk: some carry a `---` rule after the metadata, some don't. This is the Fase 1b
|
||||
* retrofit for the ~20 largest hand-written files; composeKbFile covers NEW files.
|
||||
*
|
||||
* No-op (returns content unchanged) when the file is small (≤ TOC_MIN_LINES) or already
|
||||
* carries a TOC — so it is idempotent and never churns a small file. The body below the
|
||||
* insertion point is left byte-identical: only the `## Innhold` block is added.
|
||||
*
|
||||
* @param {string} content — full existing file content
|
||||
* @returns {string} content with a `## Innhold` block inserted, or unchanged
|
||||
*/
|
||||
export function insertToc(content) {
|
||||
const s = String(content ?? '');
|
||||
if (!isLargeContent(s) || hasToc(s)) return s;
|
||||
const toc = buildToc(s);
|
||||
if (!toc) return s; // no ## sections to list — nothing to anchor a TOC to
|
||||
const lines = s.split('\n');
|
||||
let inFence = false;
|
||||
let idx = -1;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (/^\s*```/.test(lines[i])) { inFence = !inFence; continue; }
|
||||
if (inFence) continue;
|
||||
if (/^##\s+/.test(lines[i])) { idx = i; break; }
|
||||
}
|
||||
if (idx === -1) return s; // defensive: buildToc found a section we didn't — leave as-is
|
||||
const before = lines.slice(0, idx);
|
||||
while (before.length && before[before.length - 1].trim() === '') before.pop();
|
||||
const block = ['', ...toc.replace(/\n$/, '').split('\n'), ''];
|
||||
return [...before, ...block, ...lines.slice(idx)].join('\n');
|
||||
}
|
||||
|
||||
const RE_TITLE = /^#\s+\S/m;
|
||||
const RE_LAST_UPDATED = /\*\*Last updated:\*\*\s*[\d-]+/i;
|
||||
const RE_STATUS = /\*\*Status:\*\*\s*\S/i;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue