feat(ms-ai-architect): C2.2 valgfritt fritekst-felt free-context.md i onboarding (TDD) [skip-docs]
Spor C / C2.2 (#3, akseptanse K3): fanger et fritt prosa-felt i onboarding og overflater det (kappet) ambient i hver sesjon. - lib/user-data.mjs: FREE_CONTEXT_FILE (utenfor ORG_FILES — valgfri, teller ikke mot fullføring), collapseProse() surfacer fri kontekst som ÉTT felt uansett markdown-struktur (ingen stille drop), capValue() ekstrahert (DRY). - session-start-context.mjs: leser free-context.md valgfritt (ingen count++). - onboarding-agent.md: ny Phase 6 (valgfri); onboard.md: prosess/Task/status. - 11 agenter: uniform 6. bullet (grep 11/11). - Tester FØR kode: user-data 25/25 (+9), test-hooks 11/11 (+1 K3-ambient). Gates: validate 239/0/0 · kb-update 200 · kb-eval 100 · kb-integrity 192/192 (220 baseline-warns) · discovery 13/13 · gitleaks 3 pre-eksisterende. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
466094f2a2
commit
8ff73b7fe3
18 changed files with 225 additions and 11 deletions
|
|
@ -19,6 +19,7 @@ import {
|
|||
USER_DATA_DIRNAME,
|
||||
CONFIG_FILENAME,
|
||||
ORG_FILES,
|
||||
FREE_CONTEXT_FILE,
|
||||
resolveUserDataDir,
|
||||
resolveOrgDir,
|
||||
resolveConfigPath,
|
||||
|
|
@ -173,3 +174,92 @@ test('buildOrgSummary — default cap keeps the summary compact (<= 25 lines)',
|
|||
const lines = s.split('\n').filter(Boolean);
|
||||
assert.ok(lines.length <= 25, `default cap should keep <=25 lines, got ${lines.length}`);
|
||||
});
|
||||
|
||||
// ===========================================================================
|
||||
// (C) Free-prose context (C2.2) — the optional sixth file (#3, acceptance K3)
|
||||
// ===========================================================================
|
||||
|
||||
test('FREE_CONTEXT_FILE — optional free-prose filename, NOT part of ORG_FILES', () => {
|
||||
assert.equal(FREE_CONTEXT_FILE, 'free-context.md');
|
||||
assert.ok(
|
||||
!ORG_FILES.includes(FREE_CONTEXT_FILE),
|
||||
'free-context is optional and must not count toward onboarding completeness',
|
||||
);
|
||||
});
|
||||
|
||||
const FREE = [
|
||||
'---',
|
||||
'category: free-context',
|
||||
'completed: true',
|
||||
'last_updated: 2026-06-22',
|
||||
'---',
|
||||
'',
|
||||
'# Fri kontekst',
|
||||
'',
|
||||
'## Fri kontekst',
|
||||
'Vi migrerer fra on-prem til Azure i 2026.',
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
test('buildOrgSummary — surfaces free-context as a "Fri kontekst" field', () => {
|
||||
const s = buildOrgSummary({ 'free-context.md': FREE });
|
||||
assert.match(s, /Fri kontekst: Vi migrerer fra on-prem til Azure i 2026\./);
|
||||
});
|
||||
|
||||
test('buildOrgSummary — free-context is robust to raw prose without any H2 (K3: no silent drop)', () => {
|
||||
// The whole point of #3 is a single free field; it must surface even when the
|
||||
// agent writes a plain paragraph with no markdown structure at all.
|
||||
const s = buildOrgSummary({
|
||||
'free-context.md': 'Vi har hatt en hendelse med Copilot som lekket interne dokumenter.',
|
||||
});
|
||||
assert.match(s, /Fri kontekst: Vi har hatt en hendelse med Copilot/);
|
||||
});
|
||||
|
||||
test('buildOrgSummary — free-context strips frontmatter and all header lines', () => {
|
||||
const s = buildOrgSummary({
|
||||
'free-context.md': '---\ncategory: free-context\n---\n# Fri kontekst\n## Fri kontekst\nKun prosaen skal vises.\n',
|
||||
});
|
||||
assert.doesNotMatch(s, /category:/, 'frontmatter stripped');
|
||||
assert.equal(s, 'Fri kontekst: Kun prosaen skal vises.');
|
||||
});
|
||||
|
||||
test('buildOrgSummary — free-context appears AFTER the structured fields', () => {
|
||||
const out = buildOrgSummary({
|
||||
'free-context.md': '## Fri kontekst\nTilleggsnotat.',
|
||||
'organization-profile.md': PROFILE,
|
||||
});
|
||||
assert.ok(
|
||||
out.indexOf('Sektortype') < out.indexOf('Fri kontekst'),
|
||||
'structured context leads; free prose trails',
|
||||
);
|
||||
});
|
||||
|
||||
test('buildOrgSummary — long free-context is capped with an ellipsis (budget guard)', () => {
|
||||
const s = buildOrgSummary({ 'free-context.md': 'y'.repeat(500) }, { maxValueLen: 80 });
|
||||
const line = s.split('\n').find((l) => l.startsWith('Fri kontekst:'));
|
||||
assert.ok(line, 'free-context line present');
|
||||
assert.match(line, /…$/, 'truncation marker appended');
|
||||
assert.ok(line.length <= 'Fri kontekst: '.length + 80, `line too long: ${line.length}`);
|
||||
});
|
||||
|
||||
test('buildOrgSummary — empty/whitespace/header-only free-context yields no field', () => {
|
||||
assert.equal(buildOrgSummary({ 'free-context.md': ' \n\n' }), '');
|
||||
assert.equal(
|
||||
buildOrgSummary({ 'free-context.md': '---\ncategory: free-context\n---\n# Fri kontekst\n' }),
|
||||
'',
|
||||
'frontmatter + bare H1 (no prose) => nothing',
|
||||
);
|
||||
});
|
||||
|
||||
test('buildOrgSummary — free-context absent leaves the structured summary unchanged (back-compat)', () => {
|
||||
const s = buildOrgSummary({ 'organization-profile.md': PROFILE });
|
||||
assert.doesNotMatch(s, /Fri kontekst/);
|
||||
});
|
||||
|
||||
test('buildOrgSummary — realistic full onboarding keeps free-context visible (K3)', () => {
|
||||
const all = {};
|
||||
for (const f of ORG_FILES) all[f] = `## ${f}-felt\nverdi for ${f}`;
|
||||
all['free-context.md'] = '## Fri kontekst\nViktig tilleggskontekst som må være synlig.';
|
||||
const s = buildOrgSummary(all); // default cap 25
|
||||
assert.match(s, /Fri kontekst: Viktig tilleggskontekst/, 'free-context visible within default budget');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -58,6 +58,20 @@ Statlig
|
|||
Testetaten
|
||||
ORGEOF
|
||||
|
||||
# C2.2: optional free-prose file — read for the ambient summary, NOT counted
|
||||
# toward onboarding completeness. Proves K3 (free-text visible in interaction).
|
||||
cat > "$TMPHOME/.claude/ms-ai-architect/org/free-context.md" <<'FREEEOF'
|
||||
---
|
||||
category: free-context
|
||||
completed: true
|
||||
---
|
||||
|
||||
# Fri kontekst
|
||||
|
||||
## Fri kontekst
|
||||
Vi migrerer fra on-prem til Azure i 2026.
|
||||
FREEEOF
|
||||
|
||||
OUTPUT=$(CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" HOME="$TMPHOME" node "$SCRIPTS_DIR/session-start-context.mjs" 2>/dev/null)
|
||||
|
||||
if echo "$OUTPUT" | grep -q "Virksomhetskontekst (auto-injisert"; then
|
||||
|
|
@ -72,6 +86,12 @@ else
|
|||
fail "Ambient block missing org fact"
|
||||
fi
|
||||
|
||||
if echo "$OUTPUT" | grep -q "Fri kontekst: Vi migrerer fra on-prem til Azure"; then
|
||||
pass "Ambient block surfaces optional free-context (K3)"
|
||||
else
|
||||
fail "Ambient block missing free-context content (K3)"
|
||||
fi
|
||||
|
||||
# Empty home (no user org, no plugin org) => onboarding nudge, NO ambient block.
|
||||
TMPHOME2=$(mktemp -d)
|
||||
OUTPUT=$(CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" HOME="$TMPHOME2" node "$SCRIPTS_DIR/session-start-context.mjs" 2>/dev/null)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue