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
|
|
@ -32,6 +32,14 @@ export const ORG_FILES = Object.freeze([
|
|||
'business-references.md',
|
||||
]);
|
||||
|
||||
// The optional free-prose file (C2.2, #3): "everything else you want the plugin
|
||||
// to know". Deliberately NOT in ORG_FILES — its presence is optional and must
|
||||
// NOT affect the onboarding-completeness count (the SessionStart hook gates that
|
||||
// on `count < ORG_FILES.length`). It is read for the ambient summary only.
|
||||
export const FREE_CONTEXT_FILE = 'free-context.md';
|
||||
|
||||
const FREE_CONTEXT_LABEL = 'Fri kontekst';
|
||||
|
||||
const DEFAULT_SUMMARY_CAP = 25; // max summary lines (hook-injection budget)
|
||||
const DEFAULT_MAX_VALUE_LEN = 160; // per-field char cap (free-text guard)
|
||||
|
||||
|
|
@ -89,6 +97,30 @@ function extractSections(content) {
|
|||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse a markdown document to one prose line: drop frontmatter and every
|
||||
* header line, then squeeze whitespace. Used for the free-context file, which is
|
||||
* ONE free field (not structured H2 sections) and may be plain prose — so it is
|
||||
* surfaced whole rather than per-section, never silently dropped. Pure.
|
||||
* @param {string} content
|
||||
* @returns {string}
|
||||
*/
|
||||
function collapseProse(content) {
|
||||
return stripFrontmatter(content)
|
||||
.split('\n')
|
||||
.filter((line) => !/^#{1,6}\s/.test(line))
|
||||
.join(' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
/** Truncate a field value to maxValueLen, appending an ellipsis when cut. Pure. */
|
||||
function capValue(value, maxValueLen) {
|
||||
return value.length > maxValueLen
|
||||
? `${value.slice(0, maxValueLen - 1).trimEnd()}…`
|
||||
: value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a compact, deterministic org-context summary from org file contents.
|
||||
* PURE — takes a `{ filename: contentString }` map (the caller reads the files)
|
||||
|
|
@ -113,12 +145,19 @@ export function buildOrgSummary(orgFiles, opts = {}) {
|
|||
const content = orgFiles[name];
|
||||
if (typeof content !== 'string' || !content.trim()) continue;
|
||||
for (const [header, value] of extractSections(content)) {
|
||||
const trimmed =
|
||||
value.length > maxValueLen ? `${value.slice(0, maxValueLen - 1).trimEnd()}…` : value;
|
||||
fields.push(`${header}: ${trimmed}`);
|
||||
fields.push(`${header}: ${capValue(value, maxValueLen)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Free-prose context (C2.2, #3): the optional sixth file, surfaced LAST as one
|
||||
// "Fri kontekst" field regardless of internal markdown structure, so a plain
|
||||
// paragraph is never silently dropped (acceptance K3). Capped like any field.
|
||||
const freeContent = orgFiles[FREE_CONTEXT_FILE];
|
||||
if (typeof freeContent === 'string' && freeContent.trim()) {
|
||||
const body = collapseProse(freeContent);
|
||||
if (body) fields.push(`${FREE_CONTEXT_LABEL}: ${capValue(body, maxValueLen)}`);
|
||||
}
|
||||
|
||||
if (fields.length === 0) return '';
|
||||
if (fields.length <= cap) return fields.join('\n');
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue