feat(ms-ai-architect): Sesjon 19 — B3 merge-apply (cross-skill + taksonomi-persist) [skip-docs]
Merge-apply = den tyngste destruktive op-en: eksekver en operatør-godkjent merge_skills-entry → faktisk cross-skill skills/-mutasjon. Tre mekanismer som sanitize/retire ikke trengte: kuratert relokasjon (ikke arkiv), taksonomi-persistering, retire-av-absorbert. - taxonomy.mjs (ny persist): saveTaxonomy (atomisk .tmp+rename, speiler saveDecisions) + ren applyCategoryReassignments (repointer KUN der eier === from; absent/allerede-flyttet = no-op). Lag-0 er normalt read-only; ENESTE writer = gated merge-apply. - applyApprovedAction merge-gren (skill-ops.mjs): re-plan fersk → revalidér (kollisjon → guardrail.ok=false → abort) → flytt absorbed-refs → absorber (moveFile; flyttingen ER bevaringen set-equality beviser) → persister taksonomi-repoint fra FERSK plan (drift-sikker) → arkivér absorbed-SKILL.md FØR rmdir → flipp ledger approved→applied. Description-forsoning forblir MANUELL (planner-kontrakt: apply rører aldri absorber-SKILL.md). archiveMove→moveFile (generisk: arkiv + kuratert flytt). - CLI apply-skill-op.mjs merge <absorber> <absorbed> — lookup via actionKey (merge_skills:<sortert par>); default PREVIEW, --apply = dobbel-gate. TDD, tmpdir-fixtures: 0 ekte skills/-mutasjon. Ny test-skill-ops-merge-apply (7) + 2 taksonomi-tester; fjernet foreldet merge-throws fra S18-fila. Tester: kb-eval 78→84, kb-update 137→139; validate 239 · kb-integrity 192/192 uendret. eval --json deterministisk uendret (K10 eng+infra FAIL, øvrige PASS). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4ac18a76c8
commit
de3a9a483b
6 changed files with 434 additions and 55 deletions
|
|
@ -4,7 +4,7 @@
|
|||
// skill/category owns content, and update priority. poll-sitemaps,
|
||||
// discover-new-urls and report-changes READ this instead of embedding copies.
|
||||
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
|
|
@ -21,6 +21,41 @@ export function loadTaxonomy(dataDir = DEFAULT_DATA_DIR) {
|
|||
return JSON.parse(readFileSync(path, 'utf8'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the domain taxonomy atomically (write to .tmp, then rename) — mirrors
|
||||
* saveDecisions. The taxonomy is lag-0 truth and is normally read-only; the ONE
|
||||
* writer is the gated merge-apply path (Sesjon 19), which repoints category
|
||||
* ownership from an absorbed skill to its absorber. Detection scripts must NOT
|
||||
* import this.
|
||||
* @param {object} tax — taxonomy object to persist
|
||||
* @param {string} [dataDir]
|
||||
*/
|
||||
export function saveTaxonomy(tax, dataDir = DEFAULT_DATA_DIR) {
|
||||
if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });
|
||||
const path = join(dataDir, 'domain-taxonomy.json');
|
||||
const tmp = path + '.tmp';
|
||||
writeFileSync(tmp, JSON.stringify(tax, null, 2) + '\n', 'utf8');
|
||||
renameSync(tmp, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repoint category ownership for a skill merge. Pure — returns a new taxonomy,
|
||||
* mutates nothing. For each { category, from, to }, the category's owner is
|
||||
* changed to `to` ONLY when it currently equals `from` (defensive: a category
|
||||
* already repointed, or absent, is a no-op — never clobbered). All other fields
|
||||
* survive untouched.
|
||||
* @param {object} tax
|
||||
* @param {Array<{category: string, from: string, to: string}>} reassignments
|
||||
* @returns {object} new taxonomy
|
||||
*/
|
||||
export function applyCategoryReassignments(tax, reassignments) {
|
||||
const next = { ...tax.category_skill };
|
||||
for (const { category, from, to } of reassignments) {
|
||||
if (next[category] === from) next[category] = to;
|
||||
}
|
||||
return { ...tax, category_skill: next };
|
||||
}
|
||||
|
||||
/**
|
||||
* Target sitemap child-name prefixes to scan/poll.
|
||||
* @param {object} tax
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue