mirror of
https://github.com/larksuite/cli.git
synced 2026-07-12 04:14:04 +08:00
fix: close svg slides reference audit gaps
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
## Context
|
||||
- Role: reference
|
||||
- Purpose: Narrative archetypes and depth rubric consumed by the design brief prompt.
|
||||
- Source lines: 1082-1233
|
||||
- Source lines: 1082-1234
|
||||
## Content
|
||||
# Deck design reference catalog
|
||||
|
||||
@@ -156,9 +156,10 @@ THREE QUICK DISAMBIGUATIONS (where decks wrongly converge to MID)
|
||||
1. Same metric, different altitude: a board QBR shows ONE variance walk + the ask (high altitude / low density); the operating MOR behind it shows every driver four ways (low altitude / high density). Don't ship the MOR to the board or the QBR to ops.
|
||||
2. Expert vs lay on the SAME topic (ai-hardtech-pitch vs ai-101): both about AI — one is eval tables + failure-mode rates for technical partners, the other bans benchmark charts entirely. Audience, not topic, sets altitude.
|
||||
3. Pitch is NOT low-detail everywhere: stage demo-day is C1 (one sentence/slide), but the Series B/C IC pre-read is A-altitude self-read (cohort triangles, Rule-of-40, 20-page-memo-equivalent density). Same category, opposite depth — delivery_mode + audience decide.
|
||||
```
|
||||
## Source Coverage
|
||||
|
||||
- Covers manifest sections: deck_design_reference_catalog
|
||||
- Source lines: 1082-1233
|
||||
- Source lines: 1082-1234
|
||||
- Coverage mode: extracted runtime-unit content, preserving executable prompt or contract boundaries.
|
||||
- Omitted source-only artifacts: copied implementation filenames only.
|
||||
|
||||
@@ -5,10 +5,10 @@ Use this directory for local validation and provenance audit routing.
|
||||
## Read Routes
|
||||
|
||||
- Source coverage audit: `source-coverage.md`
|
||||
- Protocol validation script: `../../scripts/validate_svg_deck.mjs`
|
||||
- Bundle manifest script: `../../scripts/svg_slides_bundle.mjs`
|
||||
- Browser text bounds script: `../../scripts/svg_slides_browser_text_bounds.mjs`
|
||||
- Prompt read trace script: `../../scripts/svg_slides_prompt_trace.mjs`
|
||||
- Protocol validation script: `../../../scripts/validate_svg_deck.mjs`
|
||||
- Bundle manifest script: `../../../scripts/svg_slides_bundle.mjs`
|
||||
- Browser text bounds script: `../../../scripts/svg_slides_browser_text_bounds.mjs`
|
||||
- Prompt read trace script: `../../../scripts/svg_slides_prompt_trace.mjs`
|
||||
|
||||
## Source Slice
|
||||
|
||||
|
||||
@@ -14,6 +14,27 @@ function argValue(argv, name, fallback = "") {
|
||||
return index >= 0 ? argv[index + 1] : fallback;
|
||||
}
|
||||
|
||||
function deckUsesCharts(slideFiles) {
|
||||
return slideFiles.some((file) => fs.readFileSync(file, "utf8").includes('slide:role="chart"'));
|
||||
}
|
||||
|
||||
function promptTraceHasChartEvidence(promptTracePath) {
|
||||
const receipt = JSON.parse(fs.readFileSync(promptTracePath, "utf8"));
|
||||
const paths = new Set((receipt.read_order || []).map((entry) => entry.path));
|
||||
const hasBaseChartReads = [
|
||||
"generate-svg-chart/README.md",
|
||||
"generate-svg-chart/input-schema.md",
|
||||
"generate-svg-chart/chart-agent-contract.md",
|
||||
"generate-svg-chart/design-envelope.md",
|
||||
"generate-svg-chart/snippets/README.md"
|
||||
].every((relPath) => paths.has(relPath));
|
||||
const hasFallback = Array.isArray(receipt.fallbacks)
|
||||
&& receipt.fallbacks.includes("generate-svg-chart/full.md")
|
||||
&& paths.has("generate-svg-chart/full.md");
|
||||
const hasSnippet = [...paths].some((relPath) => relPath.startsWith("generate-svg-chart/snippets/") && relPath !== "generate-svg-chart/snippets/README.md");
|
||||
return hasBaseChartReads && (hasFallback || hasSnippet);
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const deckArg = args.find((arg) => !arg.startsWith("--"));
|
||||
const title = argValue(args, "--title");
|
||||
@@ -43,6 +64,11 @@ if (receipt.totalErrors !== 0) {
|
||||
fail(`SVG deck is not publish-ready: ${receipt.totalErrors} validation error(s)`, 1);
|
||||
}
|
||||
|
||||
const slideFiles = fs.readdirSync(slidesDir)
|
||||
.filter((file) => file.endsWith(".svg"))
|
||||
.sort()
|
||||
.map((file) => path.join(slidesDir, file));
|
||||
|
||||
const promptTracePath = path.join(root, "receipts", "prompt_read_trace.json");
|
||||
if (!fs.existsSync(promptTracePath)) {
|
||||
fail(`Missing prompt read trace receipt: ${promptTracePath}`, 1);
|
||||
@@ -57,13 +83,12 @@ if (validateTrace.status !== 0) {
|
||||
process.stderr.write(validateTrace.stderr || validateTrace.stdout);
|
||||
process.exit(validateTrace.status || 1);
|
||||
}
|
||||
|
||||
const slideFiles = fs.readdirSync(slidesDir)
|
||||
.filter((file) => file.endsWith(".svg"))
|
||||
.sort();
|
||||
if (deckUsesCharts(slideFiles) && !promptTraceHasChartEvidence(promptTracePath)) {
|
||||
fail("Prompt read trace is missing generate_svg_chart reads required by slide:role=\"chart\"", 1);
|
||||
}
|
||||
|
||||
const pages = slideFiles.map((file, index) => {
|
||||
const abs = path.join(slidesDir, file);
|
||||
const abs = file;
|
||||
const raw = fs.readFileSync(abs);
|
||||
return {
|
||||
id: path.basename(file, ".svg"),
|
||||
|
||||
@@ -25,6 +25,11 @@ const validSlide = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:slide="https:/
|
||||
</foreignObject>
|
||||
</svg>`;
|
||||
|
||||
const chartSlide = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:slide="https://slides.bytedance.com/ns" slide:role="slide" id="chart_slide" viewBox="0 0 960 540">
|
||||
<rect slide:role="background" x="0" y="0" width="960" height="540" fill="rgba(255,255,255,1)"/>
|
||||
<rect slide:role="chart" href="resources/charts/example_bar.svg" x="80" y="120" width="640" height="360"/>
|
||||
</svg>`;
|
||||
|
||||
function writeDoc(root, relPath, role, sourceLines = "1-1", body = "content") {
|
||||
const filePath = path.join(root, relPath);
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
@@ -54,8 +59,8 @@ function writeChartReferenceSet(root) {
|
||||
writeDoc(root, "generate-svg-chart/snippets/README.md", "reference", "1590-2354");
|
||||
}
|
||||
|
||||
function createPromptTrace(root, refs) {
|
||||
const result = spawnSync("node", [traceScript, root, "--task", "generate_deck", "--references", refs], { encoding: "utf8" });
|
||||
function createPromptTrace(root, refs, extraArgs = []) {
|
||||
const result = spawnSync("node", [traceScript, root, "--task", "generate_deck", "--references", refs, ...extraArgs], { encoding: "utf8" });
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
}
|
||||
|
||||
@@ -156,3 +161,29 @@ test("bundle builder rejects chart-only prompt trace for deck bundle", () => {
|
||||
assert.match(result.stderr, /task mismatch/);
|
||||
assert.equal(fs.existsSync(path.join(root, "manifest.json")), false);
|
||||
});
|
||||
|
||||
test("bundle builder rejects chart deck without chart prompt reads", () => {
|
||||
const root = tempDeck();
|
||||
const refs = tempRoot("svg-slides-bundle-refs-");
|
||||
writeDeckReferenceSet(refs);
|
||||
fs.writeFileSync(path.join(root, "slides", "slide_01.svg"), chartSlide);
|
||||
createPromptTrace(root, refs);
|
||||
const result = spawnSync("node", [script, root, "--title", "Chart Without Reads", "--references", refs], { encoding: "utf8" });
|
||||
assert.equal(result.status, 1);
|
||||
assert.match(result.stderr, /missing generate_svg_chart reads/);
|
||||
assert.equal(fs.existsSync(path.join(root, "manifest.json")), false);
|
||||
});
|
||||
|
||||
test("bundle builder accepts chart deck with deck and chart prompt reads", () => {
|
||||
const root = tempDeck();
|
||||
const refs = tempRoot("svg-slides-bundle-refs-");
|
||||
writeDeckReferenceSet(refs);
|
||||
writeChartReferenceSet(refs);
|
||||
fs.writeFileSync(path.join(root, "slides", "slide_01.svg"), chartSlide);
|
||||
createPromptTrace(root, refs, ["--use-full-fallback"]);
|
||||
const result = spawnSync("node", [script, root, "--title", "Chart With Reads", "--references", refs], { encoding: "utf8" });
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
const manifest = JSON.parse(fs.readFileSync(path.join(root, "manifest.json"), "utf8"));
|
||||
assert.equal(manifest.publish_ready, true);
|
||||
assert.equal(manifest.receipts.prompt_read_trace, "receipts/prompt_read_trace.json");
|
||||
});
|
||||
|
||||
@@ -175,9 +175,9 @@ function writeStandaloneDocs() {
|
||||
title: "Deck Design Reference Catalog",
|
||||
role: "reference",
|
||||
purpose: "Narrative archetypes and depth rubric consumed by the design brief prompt.",
|
||||
source: "1082-1233",
|
||||
source: "1082-1234",
|
||||
coverageIds: ["deck_design_reference_catalog"],
|
||||
content: lineRange(1082, 1233)
|
||||
content: lineRange(1082, 1234)
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -410,10 +410,10 @@ function writeValidationDocs() {
|
||||
"## Read Routes",
|
||||
"",
|
||||
"- Source coverage audit: `source-coverage.md`",
|
||||
"- Protocol validation script: `../../scripts/validate_svg_deck.mjs`",
|
||||
"- Bundle manifest script: `../../scripts/svg_slides_bundle.mjs`",
|
||||
"- Browser text bounds script: `../../scripts/svg_slides_browser_text_bounds.mjs`",
|
||||
"- Prompt read trace script: `../../scripts/svg_slides_prompt_trace.mjs`",
|
||||
"- Protocol validation script: `../../../scripts/validate_svg_deck.mjs`",
|
||||
"- Bundle manifest script: `../../../scripts/svg_slides_bundle.mjs`",
|
||||
"- Browser text bounds script: `../../../scripts/svg_slides_browser_text_bounds.mjs`",
|
||||
"- Prompt read trace script: `../../../scripts/svg_slides_prompt_trace.mjs`",
|
||||
"",
|
||||
"## Source Slice",
|
||||
"",
|
||||
|
||||
@@ -31,6 +31,8 @@ test("extracts runtime-unit documents and removes obsolete summaries", () => {
|
||||
assert.ok(fs.existsSync(path.join(root, "resolve-design-brief", "full.md")));
|
||||
assert.ok(fs.existsSync(path.join(root, "generate-svg-chart", "full.md")));
|
||||
assert.match(fs.readFileSync(path.join(root, "tools", "slides-edit.md"), "utf8"), /- Role: tool_contract/);
|
||||
assert.match(fs.readFileSync(path.join(root, "deck-design-reference-catalog.md"), "utf8"), /- Source lines: 1082-1234/);
|
||||
assert.match(fs.readFileSync(path.join(root, "validation", "README.md"), "utf8"), /\.\.\/\.\.\/\.\.\/scripts\/validate_svg_deck\.mjs/);
|
||||
assert.equal(fs.existsSync(path.join(root, "workflow.md")), false);
|
||||
assert.equal(fs.existsSync(path.join(root, "protocol.md")), false);
|
||||
});
|
||||
|
||||
@@ -78,20 +78,28 @@ function requiredReads(referenceRoot, task, chartType, useFullFallback) {
|
||||
}
|
||||
|
||||
if (task === "generate_deck") {
|
||||
const entries = [
|
||||
["README.md", "root routing entrypoint"],
|
||||
["system-prompt.md", "main workflow"],
|
||||
["svg-reference.md", "SVG schema and examples"],
|
||||
["deck-design-reference-catalog.md", "narrative archetypes and depth rubric"],
|
||||
["resolve-design-brief/README.md", "design brief read path"],
|
||||
["resolve-design-brief/full.md", "design brief prompt bundle"],
|
||||
["tools/slide-outline.md", "outline tool contract"],
|
||||
["tools/activate-slides-edit.md", "activate edit mode contract"],
|
||||
["tools/slides-edit.md", "SVG authoring tool contract"],
|
||||
["tools/finish-slides-edit.md", "finish edit mode contract"],
|
||||
["validation/prompt-read-trace.md", "prompt read trace receipt contract"]
|
||||
];
|
||||
if (useFullFallback || chartType) {
|
||||
const chartReads = requiredReads(referenceRoot, "generate_chart", chartType, useFullFallback);
|
||||
return {
|
||||
entries: [...entries, ...chartReads.entries],
|
||||
fallbacks: chartReads.fallbacks
|
||||
};
|
||||
}
|
||||
return {
|
||||
entries: [
|
||||
["README.md", "root routing entrypoint"],
|
||||
["system-prompt.md", "main workflow"],
|
||||
["svg-reference.md", "SVG schema and examples"],
|
||||
["deck-design-reference-catalog.md", "narrative archetypes and depth rubric"],
|
||||
["resolve-design-brief/README.md", "design brief read path"],
|
||||
["resolve-design-brief/full.md", "design brief prompt bundle"],
|
||||
["tools/slide-outline.md", "outline tool contract"],
|
||||
["tools/activate-slides-edit.md", "activate edit mode contract"],
|
||||
["tools/slides-edit.md", "SVG authoring tool contract"],
|
||||
["tools/finish-slides-edit.md", "finish edit mode contract"],
|
||||
["validation/prompt-read-trace.md", "prompt read trace receipt contract"]
|
||||
],
|
||||
entries,
|
||||
fallbacks: []
|
||||
};
|
||||
}
|
||||
|
||||
@@ -88,6 +88,24 @@ test("uses full fallback when snippet routing is uncertain", () => {
|
||||
assert.deepEqual(receipt.fallbacks, ["generate-svg-chart/full.md"]);
|
||||
});
|
||||
|
||||
test("generate_deck can include chart fallback reads when deck uses charts", () => {
|
||||
const refs = tempRoot();
|
||||
const deck = tempRoot();
|
||||
writeDeckReferenceSet(refs);
|
||||
writeChartReferenceSet(refs);
|
||||
const result = run([deck, "--task", "generate_deck", "--references", refs, "--use-full-fallback"]);
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
const receipt = readReceipt(deck);
|
||||
const paths = receipt.read_order.map((entry) => entry.path);
|
||||
assert.equal(receipt.task, "generate_deck");
|
||||
assert.ok(paths.includes("system-prompt.md"));
|
||||
assert.ok(paths.includes("generate-svg-chart/input-schema.md"));
|
||||
assert.ok(paths.includes("generate-svg-chart/full.md"));
|
||||
assert.deepEqual(receipt.fallbacks, ["generate-svg-chart/full.md"]);
|
||||
const validate = run([deck, "--validate", "--task", "generate_deck", "--references", refs]);
|
||||
assert.equal(validate.status, 0, validate.stderr || validate.stdout);
|
||||
});
|
||||
|
||||
test("fails when a required prompt file is missing", () => {
|
||||
const refs = tempRoot();
|
||||
const deck = tempRoot();
|
||||
|
||||
Reference in New Issue
Block a user