fix(okr): B1 pekerfil uten lenkeform + B5 kollisjons-guards (kuratert-vern, reservert index.md, kryss-kilde claimed) [skip-docs]
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
7f9c790f43
commit
188b534a3b
2 changed files with 124 additions and 10 deletions
|
|
@ -6,9 +6,13 @@
|
|||
// - konsept med strategisk-kontekst/-destRel skrives dit, dokumenter/-destRel dit
|
||||
// - skrevet fil = frontmatter (verbatim) + body (verbatim), trailing newline
|
||||
// - originalens sha256 er uendret etter skriv (ikke-destruktiv, SC3)
|
||||
// - peker-fil i konseptets nivaa lenker til originalen i drop-zonen (grep-bar)
|
||||
// - peker-fil i konseptets nivaa baerer original-stien i resource: UTEN noen
|
||||
// lenkeform i body (B1: en body-lenke til ikke-.md-original feller strict)
|
||||
// - mal-sti utenfor bundle-rot avvist (../-escape) og home-org-rot avvist
|
||||
// - ingen .tmp lekker (atomisk temp+renameSync)
|
||||
// - B5-guards: nekter overskriving av kuratert (ikke-ingestion) fil; tillater
|
||||
// idempotent re-skriv av egen kilde:innboks-output; avviser reservert navn
|
||||
// (index.md); avviser kryss-kilde destRel-kollisjon via claimed-registeret
|
||||
// Zero npm deps. Moenster: tests/org-profile-write.test.mjs (mkdtemp + realpath/sha).
|
||||
|
||||
import { test } from 'node:test';
|
||||
|
|
@ -89,7 +93,7 @@ test('writeConcepts: type->nivaa-ruting (strategisk-kontekst + dokumenter)', ()
|
|||
});
|
||||
});
|
||||
|
||||
test('writeConcepts: original uendret (sha256) + peker-fil lenker til drop-zone-original', () => {
|
||||
test('writeConcepts: original uendret (sha256) + peker-fil uten lenkeform (B1)', () => {
|
||||
withBundle(({ bundleRoot, inbox }) => {
|
||||
const tPath = join(inbox, 'tildeling.txt');
|
||||
writeFileSync(tPath, TILDELING);
|
||||
|
|
@ -105,13 +109,85 @@ test('writeConcepts: original uendret (sha256) + peker-fil lenker til drop-zone-
|
|||
assert.equal(sha256(tPath), before, 'original-sha256 uendret etter skriv');
|
||||
assert.ok(existsSync(tPath), 'original finnes fortsatt i drop-zonen');
|
||||
|
||||
// Peker-fil i konseptets nivaa, grep-bar lenke til originalen.
|
||||
// Peker-fil i konseptets nivaa: resource baerer stien, body har INGEN
|
||||
// lenkeform (B1: `- [x.txt](/innboks/x.txt)` er ikke .md -> feller strict).
|
||||
const level = dirname(concepts[0].destRel);
|
||||
const pointer = join(bundleRoot, level, 'tildeling.kilde.md');
|
||||
assert.ok(existsSync(pointer), 'peker-fil skrevet i konseptets nivaa');
|
||||
const pointerBody = readFileSync(pointer, 'utf8');
|
||||
assert.match(pointerBody, /\(\/innboks\/tildeling\.txt\)/, 'peker lenker til original i drop-zonen');
|
||||
assert.match(pointerBody, /^type: Notat$/m, 'peker baerer gyldig OKF-type (vokab)');
|
||||
const pointerContent = readFileSync(pointer, 'utf8');
|
||||
assert.match(pointerContent, /^resource: innboks\/tildeling\.txt$/m, 'resource baerer original-stien');
|
||||
assert.match(pointerContent, /innboks\/tildeling\.txt/, 'original-stien er grep-bar');
|
||||
assert.doesNotMatch(pointerContent, /\]\(/, 'ingen inline/bilde-lenke i pekerfila (B1)');
|
||||
assert.doesNotMatch(pointerContent, /^[ \t]{0,3}\[[^\]]+\]:/m, 'ingen referanse-definisjon i pekerfila (B1)');
|
||||
assert.doesNotMatch(pointerContent, /<a\b|<[a-z][a-z0-9+.-]*:/i, 'ingen HTML-anker/autolink i pekerfila (B1)');
|
||||
assert.match(pointerContent, /^type: Notat$/m, 'peker baerer gyldig OKF-type (vokab)');
|
||||
assert.match(pointerContent, /^kilde: innboks$/m, 'peker baerer kilde:innboks (provenans + B5-eierskap)');
|
||||
});
|
||||
});
|
||||
|
||||
// --- B5 (A2): kollisjons-guards -- treet selv skal vaere uskjermet aldri mer ---
|
||||
|
||||
const CURATED = '---\ntype: Status\ntitle: Kuratert status\n---\nHaandskrevet innhold som ALDRI skal overskrives.\n';
|
||||
|
||||
function concept(slug, destRel, sourceSlug = 'kilde-a') {
|
||||
return {
|
||||
slug,
|
||||
sourceSlug,
|
||||
destRel,
|
||||
frontmatter: '---\ntype: Dokument\nkilde: innboks\n---\n',
|
||||
body: 'innhold',
|
||||
};
|
||||
}
|
||||
|
||||
test('writeConcepts: nekter aa overskrive pre-eksisterende kuratert fil (B5)', () => {
|
||||
withBundle(({ bundleRoot }) => {
|
||||
const curatedPath = join(bundleRoot, 'dokumenter', 'status.md');
|
||||
mkdirSync(dirname(curatedPath), { recursive: true });
|
||||
writeFileSync(curatedPath, CURATED);
|
||||
|
||||
assert.throws(
|
||||
() => writeConcepts([concept('status', 'dokumenter/status.md')], { bundleRoot }),
|
||||
/kuratert|ikke-ingestion|overskriv/i,
|
||||
'skriv over kuratert fil (uten kilde:innboks) skal kaste',
|
||||
);
|
||||
assert.equal(readFileSync(curatedPath, 'utf8'), CURATED, 'kuratert fil er byte-uendret');
|
||||
});
|
||||
});
|
||||
|
||||
test('writeConcepts: tillater idempotent re-skriv av egen kilde:innboks-fil (B5)', () => {
|
||||
withBundle(({ bundleRoot }) => {
|
||||
const c = concept('notat', 'dokumenter/notat.md');
|
||||
writeConcepts([c], { bundleRoot });
|
||||
// Re-ingest (run2): samme fil skrives igjen -- skal IKKE kaste.
|
||||
writeConcepts([c], { bundleRoot });
|
||||
const written = readFileSync(join(bundleRoot, 'dokumenter', 'notat.md'), 'utf8');
|
||||
assert.ok(written.includes('kilde: innboks'), 'ingestion-eierskap staar i skrevet fil');
|
||||
});
|
||||
});
|
||||
|
||||
test('writeConcepts: reservert navn index.md avvises (B5)', () => {
|
||||
withBundle(({ bundleRoot }) => {
|
||||
assert.throws(
|
||||
() => writeConcepts([concept('index', 'dokumenter/index.md')], { bundleRoot }),
|
||||
/reservert|index/i,
|
||||
'destRel med basename index.md skal kaste (indeksering eier index.md)',
|
||||
);
|
||||
assert.ok(!existsSync(join(bundleRoot, 'dokumenter', 'index.md')), 'ingenting skrevet');
|
||||
});
|
||||
});
|
||||
|
||||
test('writeConcepts: kryss-kilde destRel-kollisjon avvises via claimed-register (B5)', () => {
|
||||
withBundle(({ bundleRoot }) => {
|
||||
const claimed = new Map();
|
||||
writeConcepts([concept('status', 'dokumenter/status.md', 'kilde-a')], { bundleRoot, claimed });
|
||||
// Annen kilde, samme destRel -> kollisjon (stille last-wins var B5-datatapet).
|
||||
assert.throws(
|
||||
() => writeConcepts([concept('status', 'dokumenter/status.md', 'kilde-b')], { bundleRoot, claimed }),
|
||||
/kollisjon|kilde/i,
|
||||
'samme destRel fra annen kilde i samme kjoering skal kaste',
|
||||
);
|
||||
// Samme kilde igjen (relasjons-omskriv i fase 2) -> OK.
|
||||
writeConcepts([concept('status', 'dokumenter/status.md', 'kilde-a')], { bundleRoot, claimed });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue