fix(okr): B2 ingestion-kode-hygiene + bump 1.7.1 (patch-lane)

8 hygiene-fikser fra review 2026-07-16 par.4 kode-lista, TDD roed-groenn
(suite 138 -> 149):
- innboks-split: slugify translittererer ae/oe (datatap-fiks)
- innboks-frontmatter: beskrivende feil ved manglende sourceMtime
- okf-index: --okf-version bumper eksisterende rot, flagg-tolerant CLI
  (exit 2 ved manglende verdi), sanitizeEntry strip C1/zero-width/bidi/
  Unicode-tag
- write-org-profile: circuit-breaker MERGER i stedet for aa overskrive
  full config (M4); test beviser at eksisterende config overlever
- compose-org-profile: intern ----linje trunkerer ikke blokken
- coaching-hook: at-risk teller status-markerte tabellrader (M1/m1)
- inject-okr-context: topic-guard treffer boeyningsformer (maalene)
- frontmatter: BOM/CRLF-toleranse (falsk mangler-type-fiks)

Versjonsflater bumpet til 1.7.1 (package/plugin/lock/CLAUDE/README/
SKILL x2/package-shape-test) + CHANGELOG 1.7.1-seksjon (B1+B2).
Release-tag + katalog-ref venter paa [G-B] operatoer-go.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-07-17 04:02:10 +02:00
commit 482effbad1
25 changed files with 289 additions and 29 deletions

View file

@ -18,7 +18,10 @@
const FM_RE = /^---\n([\s\S]*?)\n---/;
export function parseFrontmatter(content) {
const match = String(content).match(FM_RE);
// B2: toler UTF-8 BOM foran forste fence + CRLF-linjeskift (Windows-produserte
// filer) -- ellers bommer FM_RE og fila rapporteres falskt som "mangler type".
const normalized = String(content).replace(/^\uFEFF/, '').replace(/\r\n/g, '\n');
const match = normalized.match(FM_RE);
const raw = match ? match[1] : null;
const get = (key) => {

View file

@ -57,11 +57,20 @@ function deriveDescription(body) {
// concept (fra splitConcepts) -> beriket konsept med OKF-frontmatter + destRel.
export function projectFrontmatter(concept, { sourcePath, sourceMtime } = {}) {
// B2: timestamp er mtime-basert (idempotens by construction) -- en manglende/
// ugyldig sourceMtime skal feile beskrivende her, ikke som naken RangeError
// fra toISOString (og ALDRI falle tilbake til veggklokka).
const mtime = new Date(sourceMtime ?? NaN);
if (Number.isNaN(mtime.getTime())) {
throw new TypeError(
`projectFrontmatter: opts.sourceMtime maa vaere gyldig Date/ms-epoch (fikk: ${sourceMtime})`,
);
}
const resource = String(sourcePath ?? '');
const type = deriveType(concept.title, resource);
const description = deriveDescription(concept.body);
const tags = deriveTags(concept.title);
const timestamp = new Date(sourceMtime).toISOString();
const timestamp = mtime.toISOString();
const destRel = `${routeLevel(type)}/${concept.slug}.md`;
// Kanonisk noekkel-rekkefolge; description/tags utelates naar tomme.

View file

@ -8,11 +8,15 @@
// ett konsept". Innhold foer foerste heading bevares som ledende konsept (ingen
// datatap). Zero npm dependencies.
// Kebab-slug: lowercase, strip diakritika defensivt, ikke-alfanum -> '-', trim.
// Kebab-slug: lowercase, translitterer ae/oe (B2 -- NFKD dekomponerer ikke
// disse, saa uten dette droppes bokstaven: "OEkonomi" -> "konomi"), strip
// diakritika defensivt, ikke-alfanum -> '-', trim.
// Never-empty fallback 'konsept' (deterministisk; aldri tomt filnavn).
function slugify(text) {
const s = String(text)
.toLowerCase()
.replace(/\u00e6/g, 'ae')
.replace(/\u00f8/g, 'oe')
.normalize('NFKD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/[^a-z0-9]+/g, '-')