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>
72 lines
3 KiB
JavaScript
72 lines
3 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
// compose-org-profile.mjs
|
|
// Purpose: wrap the NESTED org-profile YAML body (organisasjon:/program: ...,
|
|
// read from stdin) in an OKF-compatible frontmatter document, prepending the
|
|
// three top-level OKF keys the second-brain "Documents/kb" layout expects on a
|
|
// concept file: type / resource / timestamp. Emits the full document to stdout,
|
|
// meant to be piped into write-org-profile.mjs (which writes it atomically to
|
|
// the reinstall-surviving home path ~/.claude/okr/org/profil.md).
|
|
//
|
|
// Why a dedicated composer (not lib/frontmatter.mjs writeFrontmatter): the org
|
|
// profile is NESTED (organisasjon: { navn, type }, program: { ... }). The flat
|
|
// writeFrontmatter would flatten/destroy that structure. We instead keep the
|
|
// caller's nested YAML verbatim and only PREPEND OKF keys above it.
|
|
//
|
|
// Why ONLY type/resource/timestamp top-level: the home profile is read by the
|
|
// flat parser (lib/frontmatter.mjs) in inject-okr-context.mjs via
|
|
// get('navn')/get('id')/get('domene')/... A NEW top-level key colliding with
|
|
// any of those (navn/name/id/fase/domene/sektor) would be matched FIRST and
|
|
// shadow the nested value, breaking org/cycle resolution (the Critical risk).
|
|
// type/resource/timestamp are read by NO consumer, so they are safe to prepend.
|
|
// The nested organisasjon.type ("offentlig") is untouched and still unread.
|
|
//
|
|
// Single-block invariant: the OKF keys and the nested body MUST live in ONE
|
|
// frontmatter block. A stray intermediate '---' would truncate the block the
|
|
// flat parser reads (dropping organisasjon: -> get('navn') === null -> no
|
|
// injection), so any fences the caller already put around the body are stripped
|
|
// before re-wrapping.
|
|
//
|
|
// Testable clock seam: OKR_NOW (ISO-8601) overrides the wall clock so the
|
|
// emitted timestamp is deterministic under test.
|
|
//
|
|
// Zero npm dependencies (node: builtins only). ASCII-clean identifiers.
|
|
|
|
import { readFileSync } from 'node:fs';
|
|
|
|
// OKF resource pointer: where the canonical org profile lives. A bare string
|
|
// pointer (OKF: "URL/path to the source"); no consumer reads it.
|
|
const RESOURCE = '~/.claude/okr/org/profil.md';
|
|
|
|
let body = '';
|
|
try {
|
|
body = readFileSync(0, 'utf8');
|
|
} catch {
|
|
body = '';
|
|
}
|
|
|
|
// Strip a leading/trailing frontmatter fence if the caller already wrapped the
|
|
// body, so the result is exactly one frontmatter block (see single-block note).
|
|
body = body.replace(/^\s*---\s*\r?\n/, '');
|
|
body = body.replace(/\r?\n---\s*\r?\n?\s*$/, '\n');
|
|
// B2: drop any REMAINING '---'-prefixed line inside the body -- the flat
|
|
// parser's block regex stops at the first such line, so an internal fence
|
|
// would silently truncate everything below it (see single-block note).
|
|
body = body
|
|
.split(/\r?\n/)
|
|
.filter((line) => !line.startsWith('---'))
|
|
.join('\n');
|
|
body = body.replace(/\s+$/, '');
|
|
|
|
const timestamp = process.env.OKR_NOW || new Date().toISOString();
|
|
|
|
const lines = [
|
|
'---',
|
|
'type: Organisasjonsprofil',
|
|
`resource: ${RESOURCE}`,
|
|
`timestamp: '${timestamp}'`,
|
|
];
|
|
if (body) lines.push(body);
|
|
lines.push('---', '');
|
|
|
|
process.stdout.write(lines.join('\n'));
|