@cyber-dash-tech/revela 0.17.1 → 0.17.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -253,7 +253,7 @@ new SlidePresentation();
253
253
 
254
254
  ### Layout Types
255
255
 
256
- Each `<section class="slide">` must set `slide-qa="true"` or `slide-qa="false"`. Use the QA flag on each layout marker. It must also set `data-slide-index="N"`, where `N` is the 1-based `DECKS.json` `slides[].index` value.
256
+ Each `<section class="slide">` must set `slide-qa="true"` or `slide-qa="false"`. Use the QA flag on each layout marker. It must also set `data-slide-index="N"`, where `N` is the canonical positive 1-based artifact slide identity from the approved deck plan or DOM order. Indexes must be unique and strictly increase.
257
257
 
258
258
  Normal `qa=true` content layouts should start with a slide-level title block unless the layout marker explicitly says otherwise. Use this structure as the default: an eyebrow for chapter/section context, then an `h2` that states the slide's claim or takeaway. Keep body boxes, charts, media, and text panels below or beside that title region.
259
259
 
@@ -470,7 +470,7 @@ These rules are mandatory for Summit.
470
470
 
471
471
  ### Layout Types
472
472
 
473
- Each `<section class="slide">` must set `slide-qa="true"` or `slide-qa="false"`. It must also set `data-slide-index="N"`, where `N` is the 1-based `DECKS.json` `slides[].index` value. Use the QA column to decide which value to write. Fetch any layout with the `revela-designs` tool (`action: "read"`, `layout: "<name>"`).
473
+ Each `<section class="slide">` must set `slide-qa="true"` or `slide-qa="false"`. It must also set `data-slide-index="N"`, where `N` is the canonical positive 1-based artifact slide identity from the approved deck plan or DOM order. Indexes must be unique and strictly increase. Use the QA column to decide which `slide-qa` value to write. Fetch any layout with the `revela-designs` tool (`action: "read"`, `layout: "<name>"`).
474
474
 
475
475
  Normal `qa=true` content layouts should start with a slide-level title block unless the layout marker explicitly says otherwise. Use an eyebrow for chapter/section context, then an `h2` that states the slide's claim or takeaway. Body boxes, charts, media, tables, and text panels should sit below or beside that title region rather than replacing it.
476
476
 
@@ -27,10 +27,10 @@ export async function handleHelp(
27
27
  `Run \`/revela enable\` to load Revela context without starting a workflow, or run \`/revela disable\` to pause it. Workflow commands still auto-enable Revela.\n\n` +
28
28
  `---\n\n` +
29
29
  `**Workflow**\n\n` +
30
- `1. \`init\` — discover workspace sources and capture intent\n` +
30
+ `1. \`init\` — discover sources, refresh the graph, ask key questions, and recommend next steps\n` +
31
31
  `2. \`research\` — close evidence gaps and bind support\n` +
32
- `3. \`story\` — inspect audience, thesis, claims, evidence, risks, and approval\n` +
33
- `4. \`make\` — generate deck or brief from approved story state\n` +
32
+ `3. \`story\` — inspect audience, thesis, claims, evidence, risks, and diagnostics\n` +
33
+ `4. \`make\` — generate deck or brief from canonical story state\n` +
34
34
  `5. \`review\` — comment on and inspect rendered deck artifacts\n` +
35
35
  `6. \`export\` — export deck artifacts to PDF or PPTX\n\n` +
36
36
  `---\n\n` +
@@ -38,11 +38,11 @@ export async function handleHelp(
38
38
  `\`/revela\` — show REVELA help\n` +
39
39
  `\`/revela enable\` — enable Revela prompt/context without starting a workflow\n` +
40
40
  `\`/revela disable\` — disable Revela prompt/context for this session\n` +
41
- `\`/revela init\` — initialize or refresh workspace story state\n` +
41
+ `\`/revela init\` — start Revela: discover sources, refresh story state, ask key questions, and recommend next steps\n` +
42
42
  `\`/revela research\` — research, bind evidence, and reduce story gaps\n` +
43
43
  `\`/revela story [-l language]\` — open the read-only story workspace UI\n` +
44
- `\`/revela make --deck\` — make a deck from approved story state\n` +
45
- `\`/revela make --brief [file.md]\` — render executive brief from approved story\n` +
44
+ `\`/revela make --deck\` — make a deck from story state and deck-plan/\n` +
45
+ `\`/revela make --brief [file.md]\` — render executive brief from story state\n` +
46
46
  `\`/revela review --deck\` — open deck reading, insight, and comment workspace\n` +
47
47
  `\`/revela export --deck pdf [file.html]\` — export HTML deck to PDF\n` +
48
48
  `\`/revela export --deck pptx [file.html] [--notes]\` — export HTML deck to PPTX\n` +
@@ -8,16 +8,18 @@ export function buildInitPrompt({
8
8
  workspaceRoot?: string
9
9
  }): string {
10
10
  const mode = exists
11
- ? `A ${DECKS_STATE_FILE} file already exists. Read it first through the revela-decks tool and update it conservatively.`
12
- : `No ${DECKS_STATE_FILE} file exists yet. Initialize the Markdown narrative vault through the revela-decks tool before writing narrative meaning.`
11
+ ? `A ${DECKS_STATE_FILE} file already exists as legacy/cache state. Read it first through the revela-decks tool and update it conservatively only for compatibility metadata.`
12
+ : `No ${DECKS_STATE_FILE} file exists yet. Keep this workspace file-native: initialize the Markdown narrative vault before writing narrative meaning and do not create ${DECKS_STATE_FILE}.`
13
13
 
14
- return `Initialize Revela narrative workspace state.
14
+ return `Start Revela on the current workspace.
15
15
 
16
16
  Goal:
17
- - Build or refresh ${DECKS_STATE_FILE} and the Markdown narrative vault from local workspace evidence.
17
+ - Initialize or refresh the Markdown narrative vault and file-native source inventory from local workspace evidence.
18
18
  - Treat init as repeatable ingest: discover files, register source materials, follow returned ingest task hints, and distill stable narrative meaning.
19
19
  - Capture primary audience, belief before/after, decision/action, thesis, central claims, evidence availability, objections, risks, source materials, artifact history, and open questions.
20
+ - End init with a guided completion report: what local discovery found, what the narrative graph currently contains, what gaps remain, what user clarification is needed, and which command should run next.
20
21
  - Do not treat initialization as permission to write a deck. Do not require slide count, visual style, design selection, output path, layout choices, or component choices unless the user explicitly asks to render.
22
+ - Treat central claims as chapter-ready claims, not evidence fragments: a central claim should be able to support framing/context, proof/evidence, decision implication, and explicit boundary/gap/risk material. If local material only supports a narrow fact, record it as supporting evidence or a supporting claim instead of promoting it to central importance.
21
23
 
22
24
  Current state:
23
25
  - ${mode}
@@ -32,7 +34,7 @@ Workspace boundary rules:
32
34
  - If the current workspace appears too broad, stop and ask the user which workspace subdirectory to initialize instead of scanning outside or deeply across everything.
33
35
 
34
36
  Expected tool use during init:
35
- - \`revela-decks init\` and \`revela-decks initNarrativeVault\` are expected controlled workspace-state/vault boundaries. Empty-looking optional fields in tool UI are a schema display artifact, not user-provided evidence.
37
+ - \`revela-decks init\` and \`revela-decks initNarrativeVault\` are expected controlled file-native/vault boundaries. In fresh workspaces they must not create ${DECKS_STATE_FILE}; if legacy state already exists, they may update compatibility metadata conservatively. Empty-looking optional fields in tool UI are a schema display artifact, not user-provided evidence.
36
38
  - Treat \`authoringContract\` returned by \`read(summary: true)\`, \`initNarrativeVault\`, or \`narrativeInventory\` as the Markdown authoring guide: valid node types, plain id convention, inline relation syntax, forbidden compatibility actions, and optional helper templates.
37
39
  - Treat \`markdownQa\` returned by \`read(summary: true)\`, \`compileNarrativeVault\`, or \`revela-decks markdownQa\` as post-authoring repair feedback that is separate from compiler diagnostics. Fix \`repairCards\` by smallest repair; do not invent missing claims, evidence, source paths, URLs, quotes, or caveats just to clear QA. If \`compileNarrativeVault\` output does not visibly include \`markdownQa\`, call \`revela-decks markdownQa\` before final reporting.
38
40
  - Before authoring claims, evidence, relations, objections, risks, or research gaps, inspect \`narrativeInventory\` from \`read(summary: true)\` or call \`revela-decks narrativeInventory\`. Reuse existing ids and relation targets. Do not invent evidence ids, claim ids, or relation targets before checking inventory unless you are intentionally creating the missing node in Markdown.
@@ -58,8 +60,8 @@ Required workflow:
58
60
  5. For selected relevant tasks, read directly when \`suggestedAction: "read_directly"\`; call \`revela-extract-document-materials\` first when \`suggestedAction: "extract_then_read"\`. Do not extract every document by default.
59
61
  6. Before writing narrative meaning, inspect \`narrativeInventory\` from the latest \`read(summary: true)\` result or call \`revela-decks narrativeInventory\`. Then distill stable findings into \`revela-narrative/**/*.md\` using the Markdown authoring guide. Completeness is not a gate: write partial claims, caveats, unsupported scope, and research gaps rather than waiting for a complete story. Use optional helpers such as \`upsertVaultResearchGap\`, \`upsertVaultEvidence\`, or \`bindResearchFindings\` only when they fit the exact update. Preserve frontmatter ids and existing section headings when editing Markdown. Write nodes first; add inline \`## Relations\` edges afterward only when explicit.
60
62
  7. After Markdown changes, rely on the vault write hook or call \`revela-decks markdownQa\`, then \`revela-decks compileNarrativeVault\`; keep \`markdownQa.repairCards\` separate from compiler blockers and fix both before treating the narrative as usable. If no explicit \`markdownQa\` result is visible after compile, call \`revela-decks markdownQa\` as a manual fallback. Do not use \`upsertNarrative\`.
61
- 8. If explicit deck/artifact information exists, record conservative deck specs only from visible information. Do not infer hidden evidence from generated artifacts.
62
- 9. Report initialized/updated/migrated state plus counts and paths for added, changed, newer-than-vault, unchanged, \`ingest.ingestCandidates\`, and \`ingest.suggestedTasks\`. Always include \`Markdown QA: clean\` or \`Markdown QA blockers:\` in the final report. If Markdown QA blockers remain, do not say the workspace initialized cleanly; say the vault was initialized but Markdown repairs remain.
63
+ 8. If explicit deck/artifact information exists, record conservative artifact context in file-native outputs or existing compatibility state only from visible information. Do not infer hidden evidence from generated artifacts.
64
+ 9. Complete an Init Completion Report before ending. Do not end with only a technical success message. Include local discovery counts and paths for added, changed, newer-than-vault, unchanged, \`ingest.ingestCandidates\`, and \`ingest.suggestedTasks\`; a narrative graph summary; open evidence/research gaps; any Markdown QA status; user clarification questions; and recommended next commands. Always include \`Markdown QA: clean\` or \`Markdown QA blockers:\` in the final report. If Markdown QA blockers remain, do not say the workspace initialized cleanly; say the vault was initialized but Markdown repairs remain.
63
65
 
64
66
  Evidence boundary:
65
67
  - \`workspace.sourceMaterials\` and ingest task hints are candidate context, not proof.
@@ -67,6 +69,7 @@ Evidence boundary:
67
69
  - Preserve graph meaning by writing explicit edges in node-local \`## Relations\` sections after nodes exist. Use plain node-id wikilinks and optional inline rationale.
68
70
  - Intent briefs, proposals, and user-authored plans may support audience, decision, thesis, stakeholder framing, and stated internal intent. They do not by themselves prove market size, competitor performance, product-market fit, operating-model effectiveness, or external factual claims.
69
71
  - If a source states an intended strategy but not its external factual basis, record the strategy as a claim with partial or missing support and add a research gap instead of binding it as strong evidence.
72
+ - If a central claim cannot yet sustain a future claim-led chapter, keep the insufficiency visible through evidence status, unsupported scope, caveats, or a research gap rather than padding the future deck with generic slides.
70
73
  - A successful vault compile means the vault is structurally valid. It is not evidence readiness, narrative approval, or permission to make a deck/brief.
71
74
 
72
75
  Narrative questions to ask only when missing:
@@ -79,12 +82,19 @@ Narrative questions to ask only when missing:
79
82
  - Which sources or findings support those claims?
80
83
  - What objections, risks, assumptions, or caveats could break the argument?
81
84
 
85
+ Init completion rules:
86
+ - Before ending \`/revela init\`, either use the question tool (AskQuestion) for at least one useful clarification or explicitly state that no clarification is needed now.
87
+ - Ask only narrative-startup questions during init: audience, decision/action, scope, source priority, missing internal data, or whether external research is allowed for public evidence gaps.
88
+ - Do not ask for slide count, design choice, layout choice, visual style, output path, PDF/PPTX export, or component preferences during init.
89
+ - Always surface open gaps. Classify them as evidence gaps, research gaps, internal-data-needed, source-quality limits, or user-intent questions when possible.
90
+ - Always recommend the next command: \`/revela research\` when evidence gaps need support, \`/revela story\` when the graph is ready to inspect, and \`/revela make --deck\` only when the user is ready to render from the current story.
91
+
82
92
  Memory rules:
83
- - Only write facts supported by workspace files or explicit user statements into ${DECKS_STATE_FILE} workspace state, source materials, narrative compatibility fields, deck memory, and open questions.
93
+ - Only write facts supported by workspace files or explicit user statements into file-native narrative/source files or, when ${DECKS_STATE_FILE} already exists, conservative compatibility metadata such as source materials, deck memory, and open questions.
84
94
  - Only write user preferences if the user explicitly stated that Revela should remember them.
85
95
  - Do not infer personal preferences from one-off requests.
86
96
  - Do not store secrets, credentials, API keys, tokens, account details, or sensitive personal information.
87
- - Do not mark narrative approval, render override, or writeReadiness as ready during init.
97
+ - Do not create or update approval, render override, or writeReadiness workflow state during init.
88
98
  - Treat this workspace as a single deck project. If the user wants another deck, guide them to create another workspace/folder rather than adding a second deck record.
89
99
  - If new evidence conflicts with existing memory, preserve both briefly and add an Open Question instead of silently overwriting.
90
100
 
@@ -2,11 +2,10 @@ import { mkdirSync, writeFileSync } from "fs"
2
2
  import { tmpdir } from "os"
3
3
  import { join } from "path"
4
4
  import { openUrl as defaultOpenUrl } from "../edit/open"
5
- import { hasDecksState, readDecksState } from "../decks-state"
5
+ import { createEmptyDecksState } from "../decks-state"
6
6
  import { buildNarrativeMap, formatNarrativeMap } from "../narrative-state/map"
7
7
  import { renderNarrativeMapHtmlWithDisplay } from "../narrative-state/map-html"
8
8
  import { emptyDisplayModel, type NarrativeViewLanguage, type ValidatedNarrativeDisplayModel } from "../narrative-state/display"
9
- import type { NarrativeApproval } from "../narrative-state/types"
10
9
  import { compileNarrativeVault, formatVaultDiagnosticMarkdown, formatVaultDiagnosticReport, hasNarrativeVault } from "../narrative-vault"
11
10
 
12
11
  export interface NarrativeArgs {
@@ -18,6 +17,10 @@ export interface StoryArgs {
18
17
  language: NarrativeViewLanguage
19
18
  }
20
19
 
20
+ export type StoryMapLoadResult =
21
+ | { ok: true; map: ReturnType<typeof buildNarrativeMap>; diagnosticsMarkdown: string; diagnosticsReport: ReturnType<typeof formatVaultDiagnosticReport> }
22
+ | { ok: false; error: string; diagnosticsMarkdown: string; diagnosticsReport?: ReturnType<typeof formatVaultDiagnosticReport> }
23
+
21
24
  export type ParseNarrativeArgsResult = { ok: true; args: NarrativeArgs } | { ok: false; error: string }
22
25
  export type ParseStoryArgsResult = { ok: true; args: StoryArgs } | { ok: false; error: string }
23
26
 
@@ -77,14 +80,14 @@ export async function handleNarrative(
77
80
  send: (text: string) => Promise<void>,
78
81
  ): Promise<void> {
79
82
  try {
80
- if (!hasDecksState(options.workspaceRoot)) {
81
- await send("No `DECKS.json` found. Run `/revela init` first to initialize the narrative workspace.")
83
+ const loaded = loadStoryMap(options.workspaceRoot)
84
+ if (!loaded.ok) {
85
+ await send([loaded.error, loaded.diagnosticsMarkdown].filter(Boolean).join("\n\n"))
82
86
  return
83
87
  }
84
88
 
85
- const state = readDecksState(options.workspaceRoot)
86
- const map = buildNarrativeMap(state)
87
- const diagnosticsMarkdown = vaultDiagnosticsMarkdown(options.workspaceRoot, state.narrative?.approvals ?? [])
89
+ const map = loaded.map
90
+ const diagnosticsMarkdown = loaded.diagnosticsMarkdown
88
91
  const markdown = [diagnosticsMarkdown, formatNarrativeMap(map)].filter(Boolean).join("\n\n")
89
92
 
90
93
  if (options.openBrowser) {
@@ -105,22 +108,40 @@ export async function handleNarrative(
105
108
  }
106
109
  }
107
110
 
108
- function vaultDiagnosticsMarkdown(workspaceRoot: string, fallbackApprovals: NarrativeApproval[]): string {
109
- if (!hasNarrativeVault(workspaceRoot)) return ""
110
- const result = compileNarrativeVault(workspaceRoot, { fallbackApprovals })
111
- return formatVaultDiagnosticMarkdown(formatVaultDiagnosticReport(result.diagnostics))
111
+ export function loadStoryMap(workspaceRoot: string): StoryMapLoadResult {
112
+ if (!hasNarrativeVault(workspaceRoot)) {
113
+ return {
114
+ ok: false,
115
+ error: "No `revela-narrative/` vault found. Run `/revela init` first to initialize the narrative workspace.",
116
+ diagnosticsMarkdown: "",
117
+ }
118
+ }
119
+
120
+ const result = compileNarrativeVault(workspaceRoot)
121
+ const diagnosticsReport = formatVaultDiagnosticReport(result.diagnostics)
122
+ const diagnosticsMarkdown = formatVaultDiagnosticMarkdown(diagnosticsReport)
123
+ if (!result.narrative) {
124
+ return {
125
+ ok: false,
126
+ error: "The `revela-narrative/` vault could not be compiled into a narrative map.",
127
+ diagnosticsMarkdown,
128
+ diagnosticsReport,
129
+ }
130
+ }
131
+
132
+ const state = createEmptyDecksState()
133
+ state.narrative = result.narrative
134
+ return { ok: true, map: buildNarrativeMap(state), diagnosticsMarkdown, diagnosticsReport }
112
135
  }
113
136
 
114
137
  export function buildNarrativeViewPrompt(options: { workspaceRoot: string; language: NarrativeViewLanguage }): string {
115
- if (!hasDecksState(options.workspaceRoot)) {
116
- return "No `DECKS.json` found. Tell the user to run `/revela init` before opening the narrative view. Do not call any tool."
138
+ const loaded = loadStoryMap(options.workspaceRoot)
139
+ if (!loaded.ok) {
140
+ return `${loaded.error}\n\n${loaded.diagnosticsMarkdown}\n\nDo not call any tool.`
117
141
  }
118
142
 
119
- const state = readDecksState(options.workspaceRoot)
120
- const map = buildNarrativeMap(state)
121
- const vaultDiagnostics = hasNarrativeVault(options.workspaceRoot)
122
- ? formatVaultDiagnosticReport(compileNarrativeVault(options.workspaceRoot, { fallbackApprovals: state.narrative?.approvals ?? [] }).diagnostics)
123
- : undefined
143
+ const map = loaded.map
144
+ const vaultDiagnostics = loaded.diagnosticsReport
124
145
  const projection = {
125
146
  narrativeHash: map.snapshot.narrativeHash,
126
147
  language: options.language,
@@ -161,7 +182,7 @@ Target language request: ${options.language}
161
182
  You must call the \`revela-narrative-view\` tool exactly once.
162
183
 
163
184
  Hard rules:
164
- - Do not mutate DECKS.json, deck HTML, evidence, claims, relations, approvals, or artifacts.
185
+ - Do not mutate narrative files, deck HTML, evidence, claims, relations, approvals, or artifacts.
165
186
  - Do not invent new claims, evidence, relations, slide coverage, source paths, findings files, quotes, or caveats.
166
187
  - Preserve every claimId exactly.
167
188
  - Preserve every relation endpoint exactly: fromClaimId, toClaimId, relation.
@@ -7,12 +7,11 @@
7
7
  * Example: decks/my-deck.html → decks/my-deck.pdf
8
8
  */
9
9
 
10
+ import { existsSync, readdirSync } from "fs"
10
11
  import { resolve } from "path"
11
- import { hasDecksState, readDecksState } from "../decks-state"
12
12
  import { exportToPdf } from "../pdf/export"
13
13
  import { assertExportQAPassed } from "../qa/export-gate"
14
14
  import { recordRenderedArtifact, workspaceRelative } from "../workspace-state/rendered-artifacts"
15
- import { resolveActiveHtmlDeckPath } from "../workspace-state/render-targets"
16
15
 
17
16
  export async function handlePdf(
18
17
  filePath: string,
@@ -22,15 +21,15 @@ export async function handlePdf(
22
21
  const root = resolve(workspaceRoot)
23
22
  const resolvedFile = resolvePdfDeckFile(root, filePath)
24
23
 
25
- if (!resolvedFile) {
24
+ if (!resolvedFile.ok) {
26
25
  await send(
27
- "**Usage:** `/revela export --deck pdf [file_path]`\n\n" +
28
- "Example: `/revela export --deck pdf decks/my-deck.html`"
26
+ `**PDF export cannot start**\n\n${resolvedFile.error}\n\n` +
27
+ "Usage: `/revela export --deck pdf [file_path]`"
29
28
  )
30
29
  return
31
30
  }
32
31
 
33
- const abs = resolvedFile.absoluteFile
32
+ const abs = resolvedFile.deck.absoluteFile
34
33
  await send(`Running pre-export QA for \`${abs}\`...`)
35
34
 
36
35
  try {
@@ -38,7 +37,7 @@ export async function handlePdf(
38
37
  await send(`Exporting \`${abs}\` to PDF...`)
39
38
  const result = await exportToPdf(abs)
40
39
  recordRenderedArtifact(root, {
41
- sourceHtmlPath: resolvedFile.file,
40
+ sourceHtmlPath: resolvedFile.deck.file,
42
41
  outputPath: result.outputPath,
43
42
  type: "pdf",
44
43
  actor: "revela-pdf",
@@ -56,17 +55,27 @@ export async function handlePdf(
56
55
  }
57
56
  }
58
57
 
59
- function resolvePdfDeckFile(workspaceRoot: string, filePath: string): { file: string; absoluteFile: string } | undefined {
58
+ function resolvePdfDeckFile(workspaceRoot: string, filePath: string): { ok: true; deck: { file: string; absoluteFile: string } } | { ok: false; error: string } {
60
59
  const explicit = filePath.trim()
61
60
  if (explicit) {
62
61
  const absoluteFile = resolve(workspaceRoot, explicit)
63
- return { file: workspaceRelative(workspaceRoot, absoluteFile), absoluteFile }
62
+ if (!existsSync(absoluteFile)) return { ok: false, error: `Deck HTML not found: ${explicit}` }
63
+ if (!/\.html?$/i.test(absoluteFile)) return { ok: false, error: `File must be an HTML file: ${explicit}` }
64
+ return { ok: true, deck: { file: workspaceRelative(workspaceRoot, absoluteFile), absoluteFile } }
64
65
  }
65
66
 
66
- if (!hasDecksState(workspaceRoot)) return undefined
67
- const state = readDecksState(workspaceRoot)
68
- const activePath = resolveActiveHtmlDeckPath(state)
69
- if (!activePath) return undefined
70
- const absoluteFile = resolve(workspaceRoot, activePath)
71
- return { file: workspaceRelative(workspaceRoot, absoluteFile), absoluteFile }
67
+ const htmlFiles = listDeckHtmlFiles(workspaceRoot)
68
+ if (htmlFiles.length === 0) return { ok: false, error: "No deck HTML found in decks/. Pass a file path or generate a deck first." }
69
+ if (htmlFiles.length > 1) return { ok: false, error: `Multiple deck HTML files found in decks/: ${htmlFiles.join(", ")}. Pass the deck path explicitly.` }
70
+ const absoluteFile = resolve(workspaceRoot, htmlFiles[0])
71
+ return { ok: true, deck: { file: workspaceRelative(workspaceRoot, absoluteFile), absoluteFile } }
72
+ }
73
+
74
+ function listDeckHtmlFiles(workspaceRoot: string): string[] {
75
+ const dir = resolve(workspaceRoot, "decks")
76
+ if (!existsSync(dir)) return []
77
+ return readdirSync(dir, { withFileTypes: true })
78
+ .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".html"))
79
+ .map((entry) => `decks/${entry.name}`)
80
+ .sort((a, b) => a.localeCompare(b))
72
81
  }
@@ -9,11 +9,9 @@
9
9
 
10
10
  import { existsSync, readdirSync } from "fs"
11
11
  import { relative, resolve, sep } from "path"
12
- import { hasDecksState, isDeckHtmlPath, readDecksState } from "../decks-state"
13
12
  import { assertDeckHtmlContractValid } from "../deck-html/contract"
14
13
  import { exportToPptx } from "../pptx/export"
15
14
  import { recordRenderedArtifact } from "../workspace-state/rendered-artifacts"
16
- import { resolveActiveHtmlDeckPath } from "../workspace-state/render-targets"
17
15
 
18
16
  export interface PptxArgs {
19
17
  filePath: string
@@ -23,7 +21,7 @@ export interface PptxArgs {
23
21
  export interface ResolvedPptxDeck {
24
22
  file: string
25
23
  absoluteFile: string
26
- source: "render-target" | "decks-state" | "fallback" | "file-path"
24
+ source: "discovered" | "file-path"
27
25
  }
28
26
 
29
27
  function formatSecs(ms: number): string {
@@ -47,18 +45,6 @@ export function resolvePptxDeck(workspaceRoot: string, filePath = ""): ResolvedP
47
45
  return { file: workspaceRelative(root, absoluteFile), absoluteFile, source: "file-path" }
48
46
  }
49
47
 
50
- if (hasDecksState(root)) {
51
- const state = readDecksState(root)
52
- const outputPath = resolveActiveHtmlDeckPath(state)
53
- if (outputPath && isDeckHtmlPath(outputPath)) {
54
- const absoluteFile = resolve(root, outputPath)
55
- if (existsSync(absoluteFile)) {
56
- const source = state.renderTargets.some((target) => target.type === "html_deck" && target.outputPath === outputPath) ? "render-target" : "decks-state"
57
- return { file: workspaceRelative(root, absoluteFile), absoluteFile, source }
58
- }
59
- }
60
- }
61
-
62
48
  const htmlFiles = listDeckHtmlFiles(root)
63
49
  if (htmlFiles.length === 0) {
64
50
  throw new Error("No deck HTML found in decks/. Generate a deck first or pass a file path.")
@@ -68,7 +54,7 @@ export function resolvePptxDeck(workspaceRoot: string, filePath = ""): ResolvedP
68
54
  }
69
55
 
70
56
  const absoluteFile = resolve(root, htmlFiles[0])
71
- return { file: workspaceRelative(root, absoluteFile), absoluteFile, source: "fallback" }
57
+ return { file: workspaceRelative(root, absoluteFile), absoluteFile, source: "discovered" }
72
58
  }
73
59
 
74
60
  export function buildPptxNotesPrompt(deck: ResolvedPptxDeck): string {
@@ -16,6 +16,7 @@ export function buildResearchPrompt({
16
16
  Goal:
17
17
  - Reduce open gaps, unsupported scope, weak evidence, unattached findings, and overextended relation rationale for the current story.
18
18
  - Drive research from canonical narrative gaps: unsupported central claims, objections, risks, decision questions, explicit researchGaps, and claim_chain_gap warnings.
19
+ - Drive research from central-claim chapter sufficiency, not only missing proof. Each central claim should aim for framing/background support, one or two bound evidence items or cases/quantitative signals, and implication/risk/boundary material before it is treated as deck-ready.
19
20
  - Treat /revela research as authorization to bind clearly supported findings through the safe \`bindResearchFindings\` boundary without asking for item-by-item user confirmation.
20
21
  - Preserve evidence boundaries: eliminate caveats only when evidence or narrower wording actually resolves them; otherwise keep precise caveats visible.
21
22
  - Do not write decks, briefs, or design artifacts during research.
@@ -50,6 +51,7 @@ Allowed mutations:
50
51
 
51
52
  Binding criteria:
52
53
  - supported claim id exists and is expressed as \`## Relations\` with \`- supports: [[claim-id]]\` for new evidence; quote/snippet is traceable and not invented; source URL/path/findingsFile is present; supportScope and unsupportedScope are explicit; caveat is preserved; strength is strong or useful partial; binding does not expand the claim. Frontmatter \`claimId\` is compatibility fallback, not the preferred graph source.
54
+ - For central claims, prefer evidence that helps one of the future chapter jobs: framing/background, proof/case/quantitative signal, or implication/risk/boundary. If research only finds a weak context fact, bind or record it narrowly and keep the chapter sufficiency gap open.
53
55
 
54
56
  Stop conditions:
55
57
  - No open externally researchable gaps remain.