test: pin the added-subsection note and readSkillEffective's changed-plus-added order (ship Step 7, GSTA-96)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-26 04:59:51 +00:00
1 parent 5db328f2b9
commit 01818d5dda
2 files changed
+65

No files matched your search

+64
View File
@@ -112,4 +112,68 @@ describe('shared-preamble hoist', () => {
expectAbsent(pointer, ['whose first heading names'], label(ctx));
}
});
// Generated by /ship Step 7 coverage audit (GSTA-96, PR #3453).
// Value: protects=a cut-down preamble variant that appends a subsection says so in its note, and a replace-only variant keeps the replace-only note;
// fails_when=the note collapses to "Only these subsections replace the shared ones" while office-hours appends ### Open-ended questions;
// why_new=the hoisted-equals-inline case passes for either note, since readSkillEffective accepts both; goldens cover ship only;
// seam=none; no_claim=that a model treats an added subsection differently (resolver output only)
test('a cut-down variant names an added subsection in its note, and only then', () => {
const REPLACE = 'Only these subsections replace the shared ones; the rest of this section is shared.';
const ADD = 'Only these subsections replace the shared ones or, under a new heading, add to its end; the rest of this section is shared.';
const subheadings = (section: string) => {
const heads: string[] = [];
let fence: string | null = null;
for (const line of section.split('\n')) {
const delimiter = /^ {0,3}(`{3,}|~{3,})/.exec(line)?.[1];
if (delimiter) {
if (!fence) fence = delimiter;
else if (delimiter[0] === fence[0] && delimiter.length >= fence.length) fence = null;
} else if (!fence && line.startsWith('### ')) heads.push(line);
}
return heads;
};
let adding = 0;
for (const ctx of contexts()) {
if (!hoistsPreamble(ctx)) continue;
const shared = new Map(sections(generateSharedPreambleFile(ctx)).map(section => [section.split('\n', 1)[0]!, section]));
for (const section of sections(generateSkillPreamble(ctx))) {
const note = section.includes(ADD) ? ADD : section.includes(REPLACE) ? REPLACE : null;
if (!note) continue;
const base = shared.get(section.split('\n', 1)[0]!);
expect(base, label(ctx)).toBeDefined();
const sharedHeads = new Set(subheadings(base!));
const adds = subheadings(section).some(heading => !sharedHeads.has(heading));
expect(note, label(ctx)).toBe(adds ? ADD : REPLACE);
if (adds) adding++;
}
}
expect(adding).toBeGreaterThan(0);
});
// Generated by /ship Step 7 coverage audit (GSTA-96, PR #3453).
// Value: protects=readSkillEffective rebuilds a variant section as shared A, changed B, then added C and D, in that order, and keeps the skill's own sections after it;
// fails_when=added subsections are dropped, put before the shared ones, reordered, or a changed subsection leaves the original in place;
// why_new=the generated-skill case has only office-hours' single added subsection and no variant that both changes and adds;
// seam=none; no_claim=that the generator emits this shape (the resolver cases above cover real renders)
test('readSkillEffective applies a changed subsection and appends added ones in order', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-preamble-effective-'));
try {
const note = 'Only these subsections replace the shared ones or, under a new heading, add to its end; the rest of this section is shared.';
fs.mkdirSync(path.join(root, 'sections'));
fs.writeFileSync(path.join(root, 'sections', SHARED_PREAMBLE_FILE),
'# gstack shared preamble 0123456789ab\n\n## Rules\n\nShared intro.\n\n### A\n\nShared A.\n\n### B\n\nShared B.\n');
fs.writeFileSync(path.join(root, 'SKILL.md'), [
'# fixture', '', SHARED_POINTER_HEADING, '', 'Pointer text.', '',
'## Rules', '', note, '', '### B', '', 'Changed B.', '', '### C', '', 'Added C.', '', '### D', '', 'Added D.', '',
'## Own', '', 'Own body.', '',
].join('\n'));
expect(readSkillEffective(path.join(root, 'SKILL.md'))).toBe([
'# fixture', '', '## Rules', '', 'Shared intro.', '', '### A', '', 'Shared A.', '', '### B', '', 'Changed B.', '',
'### C', '', 'Added C.', '', '### D', '', 'Added D.', '', '## Own', '', 'Own body.', '',
].join('\n'));
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
});