feat(okr): hook hybrid org-lese-fallback + node-test (SC6) [skip-docs]
This commit is contained in:
parent
e390a7a213
commit
9ab9ebe499
2 changed files with 92 additions and 3 deletions
|
|
@ -7,11 +7,22 @@
|
||||||
|
|
||||||
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
|
import { homedir } from 'node:os';
|
||||||
|
|
||||||
const cwd = process.cwd();
|
const cwd = process.cwd();
|
||||||
const configPath = join(cwd, '.claude', 'okr.local.md');
|
const projectConfigPath = join(cwd, '.claude', 'okr.local.md');
|
||||||
|
const homeConfigPath = join(homedir(), '.claude', 'okr', 'org', 'profil.md');
|
||||||
|
|
||||||
if (!existsSync(configPath)) {
|
// Hybrid org-profile resolution (most-specific-wins): project-local overrides
|
||||||
|
// the machine-global home profile. The home path is the Fase 3 migration target;
|
||||||
|
// this read is forward-compatible and stays inert until that file exists.
|
||||||
|
// NOTE: only the org PROFILE resolves to home. The cycle/work tree scan
|
||||||
|
// (okrDir, below) stays cwd-bound -- syklus data is always project-local.
|
||||||
|
const configPath = existsSync(projectConfigPath)
|
||||||
|
? projectConfigPath
|
||||||
|
: (existsSync(homeConfigPath) ? homeConfigPath : null);
|
||||||
|
|
||||||
|
if (!configPath) {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
78
tests/inject-okr-context.test.mjs
Normal file
78
tests/inject-okr-context.test.mjs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
// inject-okr-context.test.mjs
|
||||||
|
// Tester hybrid org-profil-resolusjon (SC6): prosjekt-lokal -> hjem-profil.
|
||||||
|
// Spawner hooken som subprosess med kontrollert cwd + HOME. Zero npm deps.
|
||||||
|
// Plassert i tests/ (ikke hooks/scripts/) pga. pathguard-vern av hook-mappa.
|
||||||
|
|
||||||
|
import { test } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { execFileSync } from 'node:child_process';
|
||||||
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import { join, dirname } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const HOOK = join(
|
||||||
|
dirname(fileURLToPath(import.meta.url)),
|
||||||
|
'..',
|
||||||
|
'hooks',
|
||||||
|
'scripts',
|
||||||
|
'inject-okr-context.mjs',
|
||||||
|
);
|
||||||
|
|
||||||
|
function makeHomeProfil(homeDir, navn) {
|
||||||
|
const p = join(homeDir, '.claude', 'okr', 'org');
|
||||||
|
mkdirSync(p, { recursive: true });
|
||||||
|
writeFileSync(join(p, 'profil.md'), `---\nnavn: "${navn}"\n---\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeProjectConfig(workDir, navn) {
|
||||||
|
const p = join(workDir, '.claude');
|
||||||
|
mkdirSync(p, { recursive: true });
|
||||||
|
writeFileSync(join(p, 'okr.local.md'), `---\nnavn: "${navn}"\n---\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runHook(cwd, home) {
|
||||||
|
// execFileSync returns stdout; the hook always exits 0.
|
||||||
|
return execFileSync('node', [HOOK], {
|
||||||
|
cwd,
|
||||||
|
env: { ...process.env, HOME: home },
|
||||||
|
encoding: 'utf8',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function withDirs(fn) {
|
||||||
|
const home = mkdtempSync(join(tmpdir(), 'okrhome-'));
|
||||||
|
const work = mkdtempSync(join(tmpdir(), 'okrwork-'));
|
||||||
|
try {
|
||||||
|
fn(home, work);
|
||||||
|
} finally {
|
||||||
|
rmSync(home, { recursive: true, force: true });
|
||||||
|
rmSync(work, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test('kun-hjem: leser org fra hjem-profil, ingen syklus-kontekst', () => {
|
||||||
|
withDirs((home, work) => {
|
||||||
|
makeHomeProfil(home, 'HjemOrg');
|
||||||
|
const out = runHook(work, home);
|
||||||
|
assert.match(out, /HjemOrg/, 'org skal komme fra hjem-profil naar prosjekt-config mangler');
|
||||||
|
assert.doesNotMatch(out, /Tilgjengelige kontekstfiler/, 'kun-hjem har ingen prosjekt-lokalt syklus-tre (okrDir er cwd-bundet)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('begge: prosjekt-lokal vinner over hjem (mest-spesifikk-vinner)', () => {
|
||||||
|
withDirs((home, work) => {
|
||||||
|
makeHomeProfil(home, 'HjemOrg');
|
||||||
|
makeProjectConfig(work, 'ProsjektOrg');
|
||||||
|
const out = runHook(work, home);
|
||||||
|
assert.match(out, /ProsjektOrg/, 'prosjekt-lokal skal vinne');
|
||||||
|
assert.doesNotMatch(out, /HjemOrg/, 'hjem skal ikke leses naar prosjekt-config finnes');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ingen: exit 0, tom output (graceful, regresjonsvakt)', () => {
|
||||||
|
withDirs((home, work) => {
|
||||||
|
const out = runHook(work, home);
|
||||||
|
assert.equal(out.trim(), '', 'ingen config -> tom stdout, ingen blokkering');
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue