diff --git a/.claude/skills/xlsx-workbook-template-creator/SKILL.md b/.claude/skills/xlsx-workbook-template-creator/SKILL.md index b4135629d..0c60b6985 100644 --- a/.claude/skills/xlsx-workbook-template-creator/SKILL.md +++ b/.claude/skills/xlsx-workbook-template-creator/SKILL.md @@ -42,10 +42,17 @@ Before scaffolding, answer: bash .claude/skills/xlsx-workbook-template-creator/scripts/create-template.sh # Step 2 — Review + customize the generated config -# Read references/decision-tree-single-vs-multi-turn.md FIRST -# Then edit src/config/xlsxTemplates/.js phase prompts + audit rules -# Reference: references/audit-rules-cookbook.md -# references/phase-budget-calibration.md +# READ FIRST (PR #143 canonical authority): +# - super-legal-mcp-refactored/src/config/xlsxTemplates/README.md +# (template schema, 3 trigger fields, 12 layouts, XLSX_TEMPLATE_BASE reference, +# dispatch priority matrix, extension workflow) +# - super-legal-mcp-refactored/prompts/xlsx-templates/README.md +# (prose sidecar design, consumption flow, 10 pitfalls, when to create vs reuse) +# THEN edit src/config/xlsxTemplates/.js + prose sidecars +# Skill references for PR-history lessons: +# - references/decision-tree-single-vs-multi-turn.md (single vs multi-turn decision) +# - references/phase-budget-calibration.md (PR #134 sensitivity isolation) +# - references/audit-rules-cookbook.md (canonical XLSX_TEMPLATE_BASE) # Step 3 — Validate the config against current bridge signature python3 .claude/skills/xlsx-workbook-template-creator/scripts/validate-template.py @@ -104,6 +111,13 @@ The post-PR-138 architecture provides automatic observability + persistence via ## Cross-references +### Canonical READMEs (PR #143 — READ THESE FIRST when customizing a scaffold) + +- **Template structure, triggers, layouts, audit primitives**: [`super-legal-mcp-refactored/src/config/xlsxTemplates/README.md`](../../../super-legal-mcp-refactored/src/config/xlsxTemplates/README.md) — schema, 3 trigger fields, 12 layouts, `XLSX_TEMPLATE_BASE` reference, dispatch priority matrix, extension workflow +- **Prose sidecar design**: [`super-legal-mcp-refactored/prompts/xlsx-templates/README.md`](../../../super-legal-mcp-refactored/prompts/xlsx-templates/README.md) — consumption flow, sidecar types with walkthroughs, 10 pitfalls, when to create vs reuse, multi-turn truncation gotcha + +### Skill reference docs (lessons + pitfalls from PR history) + - **PR #134** (sensitivity isolation principle): `references/phase-budget-calibration.md` - **PR #135** (envelope schema variants, Option A pivot): `references/envelope-handling-pr135.md` - **PR #138** (callerCategory + observability hooks): `references/observability-hooks-pr138.md` @@ -111,6 +125,12 @@ The post-PR-138 architecture provides automatic observability + persistence via - **Pitfalls + post-mortems**: `references/pitfalls-pr134-pr140.md` - **Pre-PR checklist**: `references/verification-checklist.md` +### Skill pointer references (point to canonical READMEs above) + +- **Triggers field catalog**: `references/triggers-field-catalog.md` → canonical README §3 +- **Sheet layout catalog**: `references/sheet-layout-catalog.md` → canonical README §4 +- **Prose sidecar design**: `references/prose-sidecar-design.md` → canonical prompts README + Related skills: - `code-execution-models` — when adding a new code-execution model (not template) - `feature-compliance-scaffold` — defensive pre-PR audit (run AFTER scaffolding) diff --git a/.claude/skills/xlsx-workbook-template-creator/references/decision-tree-single-vs-multi-turn.md b/.claude/skills/xlsx-workbook-template-creator/references/decision-tree-single-vs-multi-turn.md index eb3ebdd23..b71153272 100644 --- a/.claude/skills/xlsx-workbook-template-creator/references/decision-tree-single-vs-multi-turn.md +++ b/.claude/skills/xlsx-workbook-template-creator/references/decision-tree-single-vs-multi-turn.md @@ -5,7 +5,7 @@ | | Single-turn | Multi-turn | |---|---|---| | Template has `phaseSplit` field? | NO | YES (phase1+phase2+phase3 required; phase4+phase5 optional) | -| Bridge invocation | `xlsxRenderer/index.js:208` (single `runAnalysis(spec)`) | `xlsxRenderer/multiTurnOrchestrator.js:107` (per-phase) | +| Bridge invocation | `xlsxRenderer/index.js:210` (single `runAnalysis(spec)`) | `xlsxRenderer/multiTurnOrchestrator.js:107` (per-phase) | | `callerCategory` set in gather.js | `xlsx_single_turn` | `xlsx_multi_turn` | | Best for | Simple workbooks, ≤ 240s wall time, no specialty audit | Complex workbooks, > 240s, per-phase isolation needed | | Existing example | `session-models` (priority 10, fallback) | `full-deal-workbook` (5 phases), `lbo-focused` (4), `valuation-only` (4), `tax-memo-workbook` (4) | @@ -63,7 +63,7 @@ export default { def }; ### Single-turn invocation flow ``` -xlsxRenderer/index.js:208 +xlsxRenderer/index.js:210 └── composeWorkbookSpec(template, inputs, sessionId) └── spec.callerCategory = 'xlsx_single_turn' ← CRITICAL (PR #138) └── spec.constraints.output_format includes 'b64_xlsx' → triggers ENVELOPE_SCHEMA_XLSX diff --git a/.claude/skills/xlsx-workbook-template-creator/references/prose-sidecar-design.md b/.claude/skills/xlsx-workbook-template-creator/references/prose-sidecar-design.md new file mode 100644 index 000000000..f449ce4db --- /dev/null +++ b/.claude/skills/xlsx-workbook-template-creator/references/prose-sidecar-design.md @@ -0,0 +1,19 @@ +# Prose Sidecar Design (skill pointer) + +**Canonical authority**: [`super-legal-mcp-refactored/prompts/xlsx-templates/README.md`](../../../../super-legal-mcp-refactored/prompts/xlsx-templates/README.md) + +The canonical README covers: + +- **What sidecars are**: markdown files injected verbatim into bridge task text (NOT Python code, NOT schema specs) +- **Consumption flow**: `gather.js loadProseSidecars()` → `buildTaskText()` / `buildPhaseTaskText()` with line citations +- **4 sidecar types** with annotated walkthroughs of existing examples (cover, exec_summary, disclaimer, phase) +- **Common structural patterns**: Lead → Contents → Usage → Audit (240-337 word range) +- **10 documented pitfalls** from existing sidecar audit (markdown table conversion, formula references, terminology assumptions, etc.) +- **When to create new sidecar vs reuse**: always reuse `_disclaimer.md`; always create `-cover.md`; conditionally create `-executive-summary.md` +- **Multi-turn truncation gotcha**: 1500-char slice in final phase only (per `gather.js buildPhaseTaskText():481`) + +The scaffolder creates placeholder sidecar files with TODO content. **Replace TODO content with real prose following the patterns in §3 + §4 of the canonical README.** Existing templates (`full-deal-workbook-cover.md`, `valuation-only-cover.md`, etc.) are the canonical examples. + +## Why this skill reference is thin + +The canonical doc lives alongside the prose sidecars themselves (`prompts/xlsx-templates/README.md`), so the next engineer browsing existing sidecars sees the design guide in the same `ls` listing. Skill references duplicating content would drift; this pointer keeps the skill as the entry point but defers authority to the colocated README. diff --git a/.claude/skills/xlsx-workbook-template-creator/references/sheet-layout-catalog.md b/.claude/skills/xlsx-workbook-template-creator/references/sheet-layout-catalog.md new file mode 100644 index 000000000..6fdb79c1f --- /dev/null +++ b/.claude/skills/xlsx-workbook-template-creator/references/sheet-layout-catalog.md @@ -0,0 +1,17 @@ +# Sheet Layout Catalog (skill pointer) + +**Canonical authority**: [`super-legal-mcp-refactored/src/config/xlsxTemplates/README.md` §4 — Sheet layouts catalog](../../../../super-legal-mcp-refactored/src/config/xlsxTemplates/README.md#4--sheet-layouts-catalog) + +The canonical README enumerates: + +- **12 available layouts** in `_layouts.js LAYOUTS`: `cover`, `exec_summary`, `assumptions`, `three_statement`, `dcf`, `comps`, `lbo`, `sensitivity_2d`, `risk_register`, `sources_index`, `model_index`, `auto_per_model` +- **Per-layout reference**: what each produces, source shape requirements, constraints, which templates use each +- **3 source shapes** (string file/state key, `{ code_execution: { model_id } }`, `{ aggregate: [...] }`, `{ iterate: 'code_executions' }`) +- **`auto_per_model` special semantics**: dynamic per-model tabs with max_tabs=20 safety cap (session-models fallback only) +- **Extension workflow**: how to add a new layout (edit `_layouts.js` + update README + update renderer's openpyxl Python if needed) + +When the skill's `validate-template.py` warns "Unknown layout" (CHECK 19), it's saying the layout name isn't listed in §4. Either fix the typo OR extend §4 + `_layouts.js` if intentionally adding one. + +## Why this skill reference is thin + +The canonical doc lives alongside the templates + layouts it describes (`src/config/xlsxTemplates/README.md`), so the next engineer touching `_layouts.js` sees it in the same `ls` listing. Skill references duplicating content would drift; this pointer keeps the skill as the entry point but defers authority to the colocated README. diff --git a/.claude/skills/xlsx-workbook-template-creator/references/triggers-field-catalog.md b/.claude/skills/xlsx-workbook-template-creator/references/triggers-field-catalog.md new file mode 100644 index 000000000..eb80ffde7 --- /dev/null +++ b/.claude/skills/xlsx-workbook-template-creator/references/triggers-field-catalog.md @@ -0,0 +1,17 @@ +# Triggers Field Catalog (skill pointer) + +**Canonical authority**: [`super-legal-mcp-refactored/src/config/xlsxTemplates/README.md` §3 — Triggers field catalog](../../../../super-legal-mcp-refactored/src/config/xlsxTemplates/README.md#3--triggers-field-catalog) + +The canonical README enumerates: + +- **3 available trigger fields** that `_dispatcher.js evaluateTriggers()` reads from `sessionContext`: `invokedModels` (array), `dealType` (string), `dealSize` (number) +- **6 supported ops**: `eq`, `in`, `gte`, `lte`, `contains`, `count_gte` — with type-compatibility rules + worked examples +- **Triggers composition** (`any` vs `all` semantics + priority ranges 10-90) +- **Dispatch priority matrix** for the current 5 production templates +- **Extension workflow**: how to add a new trigger field (caller populates sessionContext + update README + no dispatcher edit needed) + +When the skill's `validate-template.py` warns "Unknown trigger field" (CHECK 18), it's saying the field isn't listed in §3. Either fix the typo OR extend §3 with the new field if intentionally adding one. + +## Why this skill reference is thin + +The canonical doc lives alongside the templates it describes (`src/config/xlsxTemplates/README.md`), so the next engineer touching `_dispatcher.js` sees it in the same `ls` listing. Skill references duplicating content would drift; this pointer keeps the skill as the entry point but defers authority to the colocated README. diff --git a/.claude/skills/xlsx-workbook-template-creator/scripts/create-template.sh b/.claude/skills/xlsx-workbook-template-creator/scripts/create-template.sh index 5ecaaccd4..74f235357 100755 --- a/.claude/skills/xlsx-workbook-template-creator/scripts/create-template.sh +++ b/.claude/skills/xlsx-workbook-template-creator/scripts/create-template.sh @@ -119,6 +119,12 @@ if [ "$DRY_RUN" = "1" ]; then echo " - Add: ${CAMEL_NAME} to ALL_TEMPLATES array" echo "Multi-turn? $([ $MULTI_TURN -eq 1 ] && echo yes || echo no)" echo "Priority: $PRIORITY" + echo "" + echo "▶▶▶ READ FIRST (PR #143 canonical authority) ▶▶▶" + echo " • super-legal-mcp-refactored/src/config/xlsxTemplates/README.md" + echo " └─ template schema, 3 trigger fields, 12 layouts, dispatch priority matrix" + echo " • super-legal-mcp-refactored/prompts/xlsx-templates/README.md" + echo " └─ prose sidecar design, consumption flow, 10 pitfalls, multi-turn truncation" exit 0 fi @@ -270,7 +276,7 @@ export const def = { citationDiscipline: XLSX_TEMPLATE_BASE.SOURCES_SHEET_SPEC, cellColoring: XLSX_TEMPLATE_BASE.CELL_COLORING, - // NO phaseSplit — this template runs as single-turn through xlsxRenderer/index.js:208 + // NO phaseSplit — this template runs as single-turn through xlsxRenderer/index.js:210 }; export default { def }; @@ -322,16 +328,25 @@ echo "Prose sidecar(s): $PROSE_DIR/${ID}-*.md" echo "Registry patched: $INDEX_FILE" echo "===================================================" echo "" +echo "▶▶▶ READ FIRST (PR #143 canonical authority) ▶▶▶" +echo " • super-legal-mcp-refactored/src/config/xlsxTemplates/README.md" +echo " └─ template schema, 3 trigger fields, 12 layouts, XLSX_TEMPLATE_BASE reference," +echo " dispatch priority matrix, extension workflow" +echo " • super-legal-mcp-refactored/prompts/xlsx-templates/README.md" +echo " └─ prose sidecar design, consumption flow, 10 pitfalls, multi-turn truncation" +echo "" echo "NEXT STEPS:" -echo " 1. Edit $TARGET_FILE — replace all TODO markers" -echo " 2. Edit prose sidecar(s) at $PROSE_DIR/${ID}-*.md — replace TODO content" +echo " 1. Edit $TARGET_FILE — replace all TODO markers (consult templates README §3 for triggers, §4 for layouts)" +echo " 2. Edit prose sidecar(s) at $PROSE_DIR/${ID}-*.md — replace TODO content (consult prompts README §3-§6)" echo " 3. Read references/audit-rules-cookbook.md if overriding XLSX_TEMPLATE_BASE defaults" if [ "$MULTI_TURN" = "1" ]; then - echo " 4. Read references/phase-budget-calibration.md for phase budget tuning" + echo " 4. Read references/phase-budget-calibration.md for phase budget tuning (PR #134 isolation principle)" fi echo " 5. Run: python3 .claude/skills/xlsx-workbook-template-creator/scripts/validate-template.py $ID" echo " 6. Run: bash .claude/skills/xlsx-workbook-template-creator/scripts/add-test-fixtures.sh $ID" echo " 7. Run: cd super-legal-mcp-refactored && node test/sdk/xlsx-renderer-integration.test.js" echo " 8. Verify gather.js sets spec.callerCategory='xlsx_$([ $MULTI_TURN -eq 1 ] && echo multi || echo single)_turn'" echo " for this template's invocations (CRITICAL — PR #138 retroactive lesson)" +echo " 9. Bump hard-coded '5 templates' count in test/sdk/xlsx-renderer-integration.test.js:120" +echo " (registry size assertion — adding 6th template requires updating this)" echo "" diff --git a/.claude/skills/xlsx-workbook-template-creator/scripts/validate-template.py b/.claude/skills/xlsx-workbook-template-creator/scripts/validate-template.py index 39c361666..dd52f0905 100755 --- a/.claude/skills/xlsx-workbook-template-creator/scripts/validate-template.py +++ b/.claude/skills/xlsx-workbook-template-creator/scripts/validate-template.py @@ -196,7 +196,7 @@ def main(): print(" [Multi-turn template detected — uses multiTurnOrchestrator.js]") else: - print(" [Single-turn template — no phaseSplit; uses xlsxRenderer/index.js:208]") + print(" [Single-turn template — no phaseSplit; uses xlsxRenderer/index.js:210]") # Check 13: registered in index.js if INDEX_FILE.exists(): @@ -309,6 +309,42 @@ def main(): "Found arrow function or 'function' keyword in template body — templates must be declarative; " "logic belongs in renderer (nodeAudit.js, mergeWorkbooks.js)") + # PR #143 — CHECK 18: trigger field catalog (soft WARN if unknown). + # Canonical set documented in src/config/xlsxTemplates/README.md §3. + # Engineer can intentionally extend (add new field to dispatcher caller's + # sessionContext + update README §3); warning surfaces the assumption. + KNOWN_TRIGGER_FIELDS = {'invokedModels', 'dealType', 'dealSize'} + trigger_fields_used = set(re.findall(r"field:\s*['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]", src)) + unknown_fields = trigger_fields_used - KNOWN_TRIGGER_FIELDS + if trigger_fields_used: + check( + f"All trigger fields are in canonical catalog ({sorted(trigger_fields_used)})", + len(unknown_fields) == 0, + f"Unknown trigger field(s): {sorted(unknown_fields)} — known set: {sorted(KNOWN_TRIGGER_FIELDS)}. " + f"If intentional, update super-legal-mcp-refactored/src/config/xlsxTemplates/README.md §3 to list " + f"the new field + caller responsibility, then re-run validator.", + severity="warning", + ) + + # PR #143 — CHECK 19: layout catalog (soft WARN if unknown). + # Canonical set documented in src/config/xlsxTemplates/README.md §4 and exported from _layouts.js LAYOUTS. + # Engineer can intentionally extend (add to _layouts.js + update README §4); warning surfaces the assumption. + KNOWN_LAYOUTS = { + 'cover', 'exec_summary', 'assumptions', 'three_statement', 'dcf', 'comps', 'lbo', + 'sensitivity_2d', 'risk_register', 'sources_index', 'model_index', 'auto_per_model', + } + layouts_used = set(re.findall(r"layout:\s*['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]", src)) + unknown_layouts = layouts_used - KNOWN_LAYOUTS + if layouts_used: + check( + f"All sheet layouts are in canonical catalog ({sorted(layouts_used)})", + len(unknown_layouts) == 0, + f"Unknown layout(s): {sorted(unknown_layouts)} — known set: {sorted(KNOWN_LAYOUTS)}. " + f"If intentional, update _layouts.js + super-legal-mcp-refactored/src/config/xlsxTemplates/README.md §4 " + f"to enumerate the new layout, then re-run validator.", + severity="warning", + ) + emit_report() def emit_report(): diff --git a/super-legal-mcp-refactored/CHANGELOG.md b/super-legal-mcp-refactored/CHANGELOG.md index 67ed3f2c6..f66da6263 100644 --- a/super-legal-mcp-refactored/CHANGELOG.md +++ b/super-legal-mcp-refactored/CHANGELOG.md @@ -4,6 +4,50 @@ All notable changes to the Super Legal MCP Server are documented in this file. ## [Unreleased] +### Added — Documentation: canonical xlsx template READMEs + skill catalog-aware validator (PR #143) + +Closes 3 content-knowledge gaps in the `xlsx-workbook-template-creator` skill (shipped in PR #142) that required engineers to read source code to find: +- The 3 available trigger fields (`invokedModels`, `dealType`, `dealSize`) +- The 12 available sheet layouts in `_layouts.js` +- The prose sidecar design patterns + consumption flow + 10 documented pitfalls + +**Single source of truth strategy**: canonical READMEs live alongside the code they describe (so the next engineer touching `_layouts.js` sees the doc in the same `ls` listing); skill references are thin pointers that defer to the canonical authority. + +**Files added (5)**: +- `src/config/xlsxTemplates/README.md` (~450 LOC, 7 sections): architecture overview + template schema + triggers field catalog + sheet layouts catalog + `XLSX_TEMPLATE_BASE` reference + single-vs-multi-turn decision + extension workflow +- `prompts/xlsx-templates/README.md` (~300 LOC, 7 sections): what sidecars are + consumption flow with line citations + sidecar types with annotated walkthroughs + structural patterns + 10 pitfalls + when to create vs reuse + multi-turn truncation gotcha (1500-char slice in final phase only) +- `.claude/skills/xlsx-workbook-template-creator/references/triggers-field-catalog.md` (skill pointer) +- `.claude/skills/xlsx-workbook-template-creator/references/sheet-layout-catalog.md` (skill pointer) +- `.claude/skills/xlsx-workbook-template-creator/references/prose-sidecar-design.md` (skill pointer) + +**Files modified (5)**: +- `.claude/skills/xlsx-workbook-template-creator/SKILL.md`: added "Canonical READMEs" subsection in Cross-references area; updated workflow Step 2 to recommend reading templates README FIRST when customizing scaffolded template +- `.claude/skills/xlsx-workbook-template-creator/scripts/create-template.sh`: added READ FIRST message pointing to both canonical READMEs (in both dry-run and actual-run output blocks) +- `.claude/skills/xlsx-workbook-template-creator/scripts/validate-template.py`: added CHECK 18 (trigger field catalog soft-warn) + CHECK 19 (layout catalog soft-warn). `KNOWN_TRIGGER_FIELDS` and `KNOWN_LAYOUTS` Python constants; warning text references the README so engineer knows where to read/update. Soft warnings (exit 1) not hard errors — engineer can intentionally extend by updating README first. +- `prompts/xlsx-templates/_cover-base.md`: added cross-link at top pointing to canonical prompts README (since `_cover-base.md` is an infrastructure reference doc NOT injected into bridge task text, this prevents engineers from mistaking it for the canonical authority) +- `CHANGELOG.md` (this entry) + +**Verification (L1-L5)**: +- L1 self-validation: 8/8 PASS — validator's new CHECK 18 + 19 do NOT regress existing 5 templates (all use canonical trigger fields + canonical layouts) +- L2 catalog-warn fires correctly: synthetic template with `field: 'unknownFieldName'` + `layout: 'fictional_layout'` produces both ⚠️ warnings with messages referencing README §3 and §4 for resolution +- L3 anchor integrity: all 7 templates README sections + 7 prompts README sections exist with matching anchors +- L4 doc accuracy spot-check: trigger fields cited in README §3 (`invokedModels`, `dealType`, `dealSize`) match exactly what `_dispatcher.js` reads from sessionContext; 12 layouts in README §4 match `_layouts.js LAYOUTS` (including `sensitivity_2d` which has a digit in name) +- L5 scaffolder READ FIRST visible: dry-run AND actual-run both show "▶▶▶ READ FIRST (PR #143 canonical authority) ▶▶▶" block with paths to both READMEs + +**Time-to-template impact** (vs PR #142 baseline): +- Mechanical work: ~30-45 min (unchanged from PR #142) +- Content work: ~1-2 hours (down from ~2-4 hours; READMEs catalog everything an engineer needs) +- **Total: ~1.5-2.5 hours** (down from ~2.5-4.5 hours pre-PR-143; closer to the original "~1-2 hours" claim that was overoptimistic about content work) + +**Honest limits**: +- L4 (real engineer using skill to ship a real template) still gated on next product demand +- Reference docs are static catalogs — when dispatcher gets a new trigger field or `_layouts.js` gets a new layout, README must be manually updated (validator's CHECK 18/19 warnings PROMPT this update via their error message) +- Prose sidecar guidance is still GUIDANCE — skill doesn't auto-generate sidecar content; engineer must write the actual prompts (this is the domain-expert work) + +**Plan**: `/Users/ej/.claude/plans/glittery-toasting-stardust.md` + +--- + ### v6.9.1 — Operator runbook readiness for `STRUCTURED_OUTPUT_ENFORCEMENT` flip: alerts + playbook + cohort plan + feature-flag truth-doc registration + dashboard panels + skill doc updates (PRs [#140](https://github.com/Number531/Legal-API/pull/140) + [#141](https://github.com/Number531/Legal-API/pull/141)) Closes the operational surface for the bridge observability v2 work (PRs #135-#139). Zero source-code changes — documentation, YAML config, and Grafana JSON only. Unblocks the `STRUCTURED_OUTPUT_ENFORCEMENT=true` production flip by closing 6 distinct operator-readiness gaps under one coherent PR. diff --git a/super-legal-mcp-refactored/prompts/xlsx-templates/README.md b/super-legal-mcp-refactored/prompts/xlsx-templates/README.md new file mode 100644 index 000000000..d1026c9ee --- /dev/null +++ b/super-legal-mcp-refactored/prompts/xlsx-templates/README.md @@ -0,0 +1,294 @@ +# xlsx Template Prose Sidecars — Design Reference + +> Canonical reference for engineers writing or modifying prose sidecars used by xlsx renderer templates. Companion to `src/config/xlsxTemplates/README.md` (template structure reference). + +## §1 — What sidecars are + +Prose sidecars are **markdown files** declared in a template's `prose: {}` block. They contain natural-language guidance text that is injected **verbatim** into the bridge's task prompt sent to the Anthropic code-execution sandbox. The model reads them as instructions + context for generating the workbook's Python build code. + +```javascript +// In a template (e.g., full-deal-workbook.js): +prose: { + cover: 'super-legal-mcp-refactored/prompts/xlsx-templates/full-deal-workbook-cover.md', + executiveSummary: 'super-legal-mcp-refactored/prompts/xlsx-templates/full-deal-workbook-executive-summary.md', + disclaimer: 'super-legal-mcp-refactored/prompts/xlsx-templates/_disclaimer.md', +} +``` + +Each path: +- Must end in `.md` (Zod schema constraint) +- Must exist on disk (Zod `checkProseFilesExist` startup check — fail-fast) +- Is read at render time + injected into the bridge task text + +### What sidecars are NOT + +- NOT Python code (renderer generates Python from the guidance) +- NOT schema specifications (those live in `_templateBase.js`) +- NOT layout definitions (those live in `_layouts.js`) +- NOT injected into the model's system prompt — they go in the per-call task + +--- + +## §2 — Consumption flow (how sidecars reach the bridge) + +``` +Template declares prose: { cover: '...', disclaimer: '...' } + │ + ▼ +gather.js loadProseSidecars(template) ← src/utils/xlsxRenderer/gather.js:201-220 + - Reads each .md file as UTF-8 string + - Returns { cover: "", disclaimer: "", ... } + - On read failure: substitutes "" + │ + ▼ +Single-turn: Multi-turn: +gather.js buildTaskText() gather.js buildPhaseTaskText() + Line 591 injects: Line 481 injects (final phase only): + "COVER PROSE:" "COVER PROSE (use this verbatim + prose.cover (full) in the cover sheet narrative):" + prose.cover.slice(0, 1500) ← TRUNCATED! + │ │ + └───────────────────────┘ + │ + ▼ + Bridge task text sent to Anthropic code-execution sandbox + │ + ▼ + Model writes openpyxl Python; reads "COVER PROSE:" section, + populates cover sheet with cells reflecting the prose guidance +``` + +### Key transformation: NONE + +Sidecar markdown is embedded as-is (raw text) inside the task prompt. The model parses it as instructions; no schema transformation happens between sidecar and bridge. This is intentional: prose changes don't require code changes. + +--- + +## §3 — Sidecar types (annotated walkthroughs) + +There are 4 types of sidecars in current use. The 5th category (`_cover-base.md`) is a reference document, NOT injected. + +### Type 1: Cover sidecars (one per template, 5 total) + +**Pattern**: Lead → Workbook contents → How to use → Audit notes + +**Word count**: 240-337 words (median 281) + +**Example: `full-deal-workbook-cover.md` (288 words)** + +```markdown +This workbook is the comprehensive M&A deal package... [Lead — 1-2 sentences] + +## Workbook contents +1. Cover (this sheet) — deal identity + workbook metadata +2. Executive Summary — investment thesis + headline metrics +3. Assumptions — input parameters with citations +... [numbered list — 6-9 items] + +## How to use this workbook +- BLUE cells = hardcoded inputs (verify against Sources sheet citations) +- BLACK cells = formulas (do not contain literal numbers) +- GREEN cells = cross-sheet references +... [bullet list — cell discipline + adjustment patterns] + +## Audit status +This workbook ran with audit_status = PASS at generation time... +Each code_execution_id in the Sources sheet maps to forensic detail... +``` + +**Template-specific variations**: +- `full-deal-workbook-cover.md`: 4 agents (financial, equity, risk, tax) + M&A scope +- `lbo-focused-cover.md`: M21 model focus + sponsor IRR + ICR covenant checks +- `valuation-only-cover.md`: mid-market constraints ($50M-$500M) + valuation triangulation +- `tax-memo-workbook-cover.md`: tax-structure agents + IRC section citations +- `session-models-cover.md`: auto-generated tabs + 20-tab safety cap + fallback rationale + +### Type 2: Executive summary sidecars (1 currently — full-deal only) + +**Pattern**: more prescriptive than cover. Specifies exact section structure (A through E) + formula references. + +**Example: `full-deal-workbook-executive-summary.md` (288 words)** + +```markdown +The executive summary tab is a deal-level synthesis... [Lead] + +## Section A: Investment Thesis +2-4 sentences from memo-state.json.thesis + +## Section B: Headline Financial Metrics +| Metric | Value | Formula | +|---|---|---| +| Enterprise Value | $X | =DCF!B[row] | +| Equity Value | $X | =EnterpriseValue - NetDebt | +| EV/EBITDA | Xx | =EnterpriseValue / EBITDA_TTM | +... [specific formulas] + +## Section C: Key Findings +3-7 bullets from risk-aggregator-state.json... + +## Section D: Recommendation +1-2 sentences (PROCEED / PROCEED WITH CONDITIONS / DECLINE) + +## Section E: Workbook Navigation +[Hyperlinks to other tabs] + +## Formula and style discipline +- GREEN for cross-sheet references +- BLACK for narrative text +- No hardcoded numbers in formulas +``` + +**When to write a new exec_summary sidecar**: only if your template's exec_summary tab needs prescriptive section structure beyond what's auto-derivable. Otherwise, the cover sidecar typically suffices. + +### Type 3: Phase sidecars (multi-turn templates only) + +Currently no multi-turn template uses per-phase prose sidecars in production (phase logic is encoded in `phaseSplit.phaseN.sheets` instead). The scaffolder creates `-phase1.md`, `-phase2.md`, `-phase3.md` placeholders for future use; you can delete them and remove the `prose.phaseN` entries if your phases don't need extra guidance. + +### Type 4: Disclaimer sidecar (shared, 1 file) + +**Sidecar**: `_disclaimer.md` (201 words) + +**Used by**: ALL 5 templates (shared via prose key `disclaimer`) + +**Content**: +- Addressed to end-user reading the workbook (legal/audit boilerplate tone) +- Lists what the workbook is NOT (legal advice, investment advice, etc.) +- Defines cell color semantics (BLUE/BLACK/GREEN/RED) +- Provenance transparency note (code_execution_id UUID in Sources sheet) +- Contact point (analyst or administrator) + +**Why it's shared**: identical content applies to every template. **Reuse this sidecar** in your new template's `prose.disclaimer` field rather than duplicating. + +--- + +## §4 — Common structural patterns + +All sidecars follow a similar layout regardless of type: + +| Section | Purpose | Typical length | +|---|---|---| +| **Lead** | 1-2 sentences establishing purpose + target audience | 1 paragraph | +| **Contents** | Enumerated list of what the tab shows / contains | bulleted/numbered list | +| **Usage / How to read** | Cell color semantics + adjustment patterns + what to look for | 4-8 bullet points | +| **Audit notes** | Brief explanation of audit pass/fail + provenance traceability | 1-2 paragraphs | + +**Tone**: instructive but not bossy. Implicit "you should..." rather than "MUST". Written to communicate intent to the model, not coerce behavior. + +**Format**: markdown headers (`##`, `###`), bullet lists, occasional tables (especially for metric/formula references). + +--- + +## §5 — Pitfalls (from existing sidecar audit) + +### Pitfall 1: Markdown tables don't auto-convert to Excel tables +Sidecar table formatting is guidance only. The model parses the table semantically and reconstructs as Excel cells; complex multi-column markdown tables may lose nuance. Keep tables simple (2-4 columns). + +### Pitfall 2: Specifying exact formulas in sidecar +The exec_summary sidecar shows formulas like `=DCF!B[row]`. If actual DCF tab uses different row indices, the formula reference is stale. **Mitigation**: use named ranges in sidecar formula examples (`=NPV_TotalValue`) so they survive layout changes. + +### Pitfall 3: Disclaimer references features not in all templates +`_disclaimer.md` mentions `/session-diagnostics` skill + forensic endpoint. Not all sessions have forensic access enabled. Customer reading the disclaimer may expect features that aren't available. **Mitigation**: keep shared `_disclaimer.md` generic; template-specific caveats go in template's cover sidecar. + +### Pitfall 4: Multi-turn truncation (1500-char slice in final phase) +For multi-turn templates, `gather.js buildPhaseTaskText():481` slices the prose to 1500 chars AND only injects in the FINAL phase. Earlier phases (1-N-1) don't see the prose at all. **Mitigation**: if phases 1-4 need cover context, replicate the key facts in the phase task text directly (not in prose sidecar). + +### Pitfall 5: Asymmetric sidecar coverage between templates +`full-deal-workbook` has dedicated `executive-summary.md`. `tax-memo-workbook` has an exec_summary tab in its sheets list but no exec_summary sidecar. If you copy a template structure, ensure all referenced sidecars exist or remove the `prose.` entries. Zod will reject on startup if a `prose.` path doesn't exist. + +### Pitfall 6: Cell coloring hex codes +Sidecars reference BLUE/BLACK/GREEN/RED by name. Exact hex codes (`B4C7E7`, `0000FF`, etc.) live in `_templateBase.js CELL_COLORING`. Don't restate hex codes in sidecars — they'll drift. The model resolves color names against `data.discipline` at render time. + +### Pitfall 7: Data source paths not validated +Exec summary sidecar references `memo-state.json` + `risk-aggregator-state.json`. `gather.js:179-192` makes both non-fatal (logs warning if missing). If your sidecar references a data file that doesn't exist for the session, model handles gracefully (no narrative) instead of crashing. + +### Pitfall 8: Financial/tax terminology assumes domain familiarity +Sidecars mention "§382", "§368", "§338", "EBITDA", "MOIC", "ICR floor", "continuity of interest". Model needs domain training to interpret correctly. Verify with L4 (real render) that your domain-specific sidecar produces correct content. Add explicit definitions in sidecar if a term is rare. + +### Pitfall 9: Shared disclaimer drift risk +If `tax-memo` needs a specific IRC limitation caveat, you can either: +- Add to shared `_disclaimer.md` (risks over-disclaiming for simpler templates) — DON'T +- Create `tax-memo-workbook-disclaimer.md` (breaks shared pattern) — DO + +Always create template-specific disclaimer sidecar if you need to add template-specific caveats. + +### Pitfall 10: No versioning or deprecation mechanism +Sidecars don't have version headers. If a sidecar references an old API or deprecated tab, no flag tells the next engineer. **Mitigation**: when you change a template's sheet layout, audit its prose sidecars to ensure references still apply. + +--- + +## §6 — When to create a new sidecar vs reuse existing + +### Always reuse for new templates + +| Sidecar | When to reuse | +|---|---| +| `_disclaimer.md` | ALWAYS — same legal/audit boilerplate applies to every workbook. Reference via `prose.disclaimer`. | + +### Sometimes reuse / sometimes create new + +| Sidecar | Decision | +|---|---| +| `_cover-base.md` | Reference doc only (NOT in prose blocks). Don't include in template's prose. Use as a structural guide when writing your `-cover.md` | + +### Always create new (one per template) + +| Sidecar type | Why | +|---|---| +| `-cover.md` | Template-specific use case + tab list + adjustment patterns. Required field in `prose.cover`. | +| `-executive-summary.md` | Only if your template's exec_summary tab needs prescriptive section structure (full-deal does; others don't) | + +### Decision tree for new template + +1. Will the workbook produce a cover sheet? + - YES → create `-cover.md`. Always. +2. Does the workbook produce an executive summary tab needing structured sections? + - YES → create `-executive-summary.md`. Add `prose.executiveSummary` entry. + - NO → cover sidecar mentions the exec summary briefly; no separate sidecar needed. +3. Does the workbook need template-specific caveats beyond the shared disclaimer? + - YES → create `-disclaimer.md`, use it INSTEAD of `_disclaimer.md` (don't both) + - NO → reuse `_disclaimer.md` via `prose.disclaimer`. + +--- + +## §7 — Multi-turn truncation gotcha (1500-char slice) + +For multi-turn templates, `gather.js buildPhaseTaskText()` at line 481 implements: + +```javascript +'COVER PROSE (use this verbatim in the cover sheet narrative if you add cover):', +(prose.cover || '(no cover prose)').slice(0, 1500), +``` + +This means: +- **Only the final phase** sees the cover prose +- **Truncated to 1500 chars** (≈ first 250 words of your sidecar) +- Earlier phases (1-N-1) don't see the cover prose at all + +**Implications**: +- Put the most important cover content in the **first 250 words** of `-cover.md` +- Don't bury critical guidance in section 4 or 5 of a long cover sidecar +- For cover-relevant context that earlier phases need, replicate the key fact in the phase's task text directly (orchestrator-side) or in the phase's own prose sidecar if you create one + +--- + +## Existing sidecar files + +| File | Type | Length | Templates using | +|---|---|---|---| +| `_disclaimer.md` | disclaimer (shared) | 201 words | all 5 templates | +| `_cover-base.md` | infrastructure reference (NOT injected) | 308 words (incl. PR #143 cross-link header) | none — reference doc for engineers | +| `full-deal-workbook-cover.md` | cover | 288 words | full-deal-workbook | +| `full-deal-workbook-executive-summary.md` | exec summary | 288 words | full-deal-workbook | +| `lbo-focused-cover.md` | cover | 281 words | lbo-focused | +| `session-models-cover.md` | cover | 337 words | session-models | +| `tax-memo-workbook-cover.md` | cover | 263 words | tax-memo-workbook | +| `valuation-only-cover.md` | cover | 240 words | valuation-only | + +--- + +## Related references + +- **Template schema + triggers + layouts** — `super-legal-mcp-refactored/src/config/xlsxTemplates/README.md` +- **Skill workflow + verification checklist** — `.claude/skills/xlsx-workbook-template-creator/SKILL.md` +- **Bridge envelope contract (how the model's Python output is parsed)** — `super-legal-mcp-refactored/docs/code-execution-enhancements/anthropic-sdk-best-practices-research.md` +- **PR #134 phase split lesson (informs how prose is split across phases)** — `.claude/skills/xlsx-workbook-template-creator/references/phase-budget-calibration.md` diff --git a/super-legal-mcp-refactored/prompts/xlsx-templates/_cover-base.md b/super-legal-mcp-refactored/prompts/xlsx-templates/_cover-base.md index e2253d1f3..f8fee75c2 100644 --- a/super-legal-mcp-refactored/prompts/xlsx-templates/_cover-base.md +++ b/super-legal-mcp-refactored/prompts/xlsx-templates/_cover-base.md @@ -1,5 +1,7 @@ # Cover Sheet — Base Scaffolding +> **NOTE**: This file is a structural reference document for engineers writing cover sidecars. It is NOT injected into bridge task text (no template's `prose.cover` points here). For the canonical prose sidecar design guide — including consumption flow, sidecar types, pitfalls, and when to create vs reuse — see [`README.md`](./README.md) in this directory. + The Cover sheet is the first tab in every workbook. It establishes: - **Deal identity:** Name, parties, structure, target close date diff --git a/super-legal-mcp-refactored/src/config/xlsxTemplates/README.md b/super-legal-mcp-refactored/src/config/xlsxTemplates/README.md new file mode 100644 index 000000000..6591e683d --- /dev/null +++ b/super-legal-mcp-refactored/src/config/xlsxTemplates/README.md @@ -0,0 +1,364 @@ +# xlsx Templates — Reference Catalog + +> Canonical reference for engineers adding or modifying xlsx renderer templates. Companion to the `xlsx-workbook-template-creator` skill (at `.claude/skills/xlsx-workbook-template-creator/`), which scaffolds new templates and runs Zod-aware validation. + +## §1 — Architecture overview + +Templates are **declarative data structures** that describe how to render a session into an Excel workbook. Each template lives in this directory as a single `.js` file exporting `def`. The dispatcher (`_dispatcher.js`) picks the highest-priority template whose `triggers` match the session context; the renderer then composes a spec from the template + session inputs and invokes the code-execution bridge with appropriate prose sidecars. + +### Registration flow + +``` +src/config/xlsxTemplates/.js ──┐ + │ + ▼ + index.js ALL_TEMPLATES list + │ + ▼ + index.js validates via templateSchema (Zod) + │ + ▼ + XLSX_TEMPLATES registry (Object) + │ + ▼ + selectTemplate(sessionContext) at runtime + │ + ▼ + multiTurnOrchestrator (phaseSplit present) + OR + xlsxRenderer/index.js (single-turn) +``` + +### Files in this directory + +| File | Purpose | +|---|---| +| `index.js` | Registers all templates, validates via Zod, exports `XLSX_TEMPLATES` registry + `selectTemplate()` | +| `_dispatcher.js` | Generic trigger evaluator (`evaluateTriggers`) — supports 6 ops | +| `_layouts.js` | 12 reusable sheet layout specs referenced by `template.sheets[].layout` | +| `_templateBase.js` | `XLSX_TEMPLATE_BASE` shared constants (PRE_2019_FUNCTIONS, CELL_COLORING, SOURCES_SHEET_SPEC, AUDIT_CHECKS) | +| `session-models.js` | Priority-10 fallback (any session with ≥1 invoked model) | +| `valuation-only.js` | Priority-50 (M05/M07/M08/M09 models, $50M-$500M deals) | +| `full-deal-workbook.js` | Priority-70 (M&A deals ≥ $500M) | +| `lbo-focused.js` | Priority-80 (LBO/take-private/carveout dealType OR M21 invoked) | +| `tax-memo-workbook.js` | Priority-90 (M28-M31 tax models) | + +### Adding a new template + +Use the scaffolder: + +```bash +bash .claude/skills/xlsx-workbook-template-creator/scripts/create-template.sh +``` + +It generates the boilerplate, registers the template in `index.js`, and creates prose sidecar stubs. Then read sections §3, §4, §5 below to fill in triggers + sheets + audit defaults. + +--- + +## §2 — Template schema (Zod) + +Canonical source: `src/schemas/xlsxTemplateSchema.js` (Zod `templateSchema` with `.strict()` — rejects unknown fields at runtime). + +### Required fields (10) + +| Field | Type | Constraint | +|---|---|---| +| `id` | string | kebab-case regex `^[a-z][a-z0-9-]*$` | +| `name` | string | Human-readable display name | +| `version` | string | Semver-ish | +| `triggers` | object | See §3 — `{ any?, all?, priority: 0-100 }` | +| `filenamePattern` | string | e.g., `'deal-workbook.xlsx'` | +| `prose` | object | Map of sidecar keys to `.md` paths; **all paths must exist on disk** (Zod startup check) | +| `sheets` | array | ≥1 entry; each `{ id, layout, source, conditional? }` | +| `formulaWhitelist` | string[] | ≥1 entry; typically `XLSX_TEMPLATE_BASE.PRE_2019_FUNCTIONS` | +| `citationDiscipline` | object | Pass-through; typically `XLSX_TEMPLATE_BASE.SOURCES_SHEET_SPEC` | +| `cellColoring` | object | Pass-through; typically `XLSX_TEMPLATE_BASE.CELL_COLORING` | + +### Optional fields (3) + +| Field | Type | When to set | +|---|---|---| +| `description` | string | Recommended — 1-line purpose statement | +| `methodology` | string | For templates with bespoke methodology notes | +| `phaseSplit` | object | **Presence makes template multi-turn** — see §6 | + +### Hard constraints (Zod enforces at startup) + +- `.strict()` mode — unknown top-level fields rejected +- All `prose.` file paths must exist on disk (`checkProseFilesExist`) +- No function fields anywhere (recursive `hasFunctions` rejection) — templates are pure data +- `triggers.priority` integer in [0, 100] +- Per-phase `estimated_seconds` in [1, 1200] + +--- + +## §3 — Triggers field catalog + +Source of truth: `_dispatcher.js evaluateTriggers()` — reads `sessionContext[clause.field]` for each clause. + +### Available trigger fields (currently 3) + +| Field | Type | Read from sessionContext | Used by templates | +|---|---|---|---| +| `invokedModels` | array of strings (model IDs like `'M07'`, `'M21'`) | `sessionContext.invokedModels` | all 5 templates | +| `dealType` | string | `sessionContext.dealType` | `lbo-focused`, `full-deal-workbook` | +| `dealSize` | number (USD) | `sessionContext.dealSize` | `valuation-only`, `full-deal-workbook` | + +> **Extending the catalog**: if your new template needs a different field (e.g., `clientId`, `sector`, `dealComplexity`), you must (1) ensure the caller populates `sessionContext` with that field, (2) update this README to list it, (3) the dispatcher passes the field through automatically — no `_dispatcher.js` edit needed. + +### Supported ops (6, from `SUPPORTED_OPS` in `_dispatcher.js`) + +| Op | Semantics | Compatible field types | Example | +|---|---|---|---| +| `eq` | Strict equality (`===`) | any primitive | `{ field: 'dealType', op: 'eq', value: 'M&A' }` | +| `in` | Array membership (`value.includes(actual)`) | actual: any; value: array | `{ field: 'dealType', op: 'in', value: ['M&A','LBO'] }` | +| `gte` | Numeric ≥ | number | `{ field: 'dealSize', op: 'gte', value: 500_000_000 }` | +| `lte` | Numeric ≤ | number | `{ field: 'dealSize', op: 'lte', value: 500_000_000 }` | +| `contains` | `actual.includes(value)` | actual: array; value: primitive | `{ field: 'invokedModels', op: 'contains', value: 'M21' }` | +| `count_gte` | `actual.length >= value` | actual: array; value: number | `{ field: 'invokedModels', op: 'count_gte', value: 1 }` | + +> Type mismatches return `false` (silent fail, no throw). Unknown ops throw `Error: Unsupported trigger op`. + +### Trigger composition: `any` vs `all` + +```javascript +triggers: { + any: [ , , ... ], // OR — at least one must match + all: [ , , ... ], // AND — every clause must match + priority: <0-100>, // higher wins on multiple matches +} +``` + +Template matches iff `(no any OR at least one any matches) AND (no all OR every all matches)`. + +### Dispatch priority matrix (current 5 templates) + +| Priority | Template | Triggers (any) | Triggers (all) | +|---|---|---|---| +| 90 | `tax-memo-workbook` | invokedModels contains M28\|M29\|M30\|M31 | — | +| 80 | `lbo-focused` | dealType in [LBO,take-private,carveout] OR invokedModels contains M21 | — | +| 70 | `full-deal-workbook` | dealType in [M&A,merger,acquisition] | dealSize gte $500M | +| 50 | `valuation-only` | invokedModels contains M05\|M07\|M08\|M09 | dealSize gte $50M AND dealSize lte $500M | +| 10 | `session-models` (fallback) | invokedModels count_gte 1 | — | + +**Picking priority for a new template**: specialty (deal-type or model-specific) usually 60-90; mid-range 30-50; ultra-generic fallback 10 (only `session-models` should hold priority 10). If your template would tie with an existing one, bump priority by 5 either direction depending on which should win. + +### Fallback behavior + +`selectTemplate()` returns `null` when no template matches. The renderer must handle null (e.g., skip workbook generation). Current architecture: `session-models` priority-10 catches every session with ≥1 invoked model, so `null` is rare in practice. + +### Worked trigger examples + +```javascript +// Single-condition specialty (e.g., new tax-memo variant) +triggers: { + any: [{ field: 'invokedModels', op: 'contains', value: 'M50' }], + priority: 85, +} + +// Range-bound (e.g., micro-deal model) +triggers: { + all: [ + { field: 'dealSize', op: 'gte', value: 10_000_000 }, + { field: 'dealSize', op: 'lte', value: 50_000_000 }, + ], + priority: 40, +} + +// Multi-trigger (deal type OR specific model) +triggers: { + any: [ + { field: 'dealType', op: 'in', value: ['venture','growth'] }, + { field: 'invokedModels', op: 'contains', value: 'M22' }, + ], + all: [{ field: 'dealSize', op: 'lte', value: 200_000_000 }], + priority: 60, +} +``` + +--- + +## §4 — Sheet layouts catalog + +Source of truth: `_layouts.js LAYOUTS` object. 12 layouts total. + +A template's sheet array references layouts by name: + +```javascript +sheets: [ + { id: 'cover', layout: 'cover', source: 'memo-state.json' }, + { id: 'dcf', layout: 'dcf', source: { code_execution: { model_id: 'M07' } } }, + { id: 'sources', layout: 'sources_index', source: 'consolidated-footnotes.md' }, +] +``` + +### Layout reference + +| Layout | Produces | Source shape | Constraints | Used by | +|---|---|---|---|---| +| `cover` | Title page (rows 1-16: deal/workbook/prepared-by sections) | string (e.g., `'memo-state.json'`) | freezeRow:5, columnWidths:[40,60], style:'cover' | all 5 templates | +| `exec_summary` | Narrative executive summary tab | string (e.g., `'memo-state.json'`) | freezeRow:1, autoSize:true, style:'narrative' | tax-memo, full-deal | +| `assumptions` | 5-column table (Assumption/Value/Unit/Citation Tag/Source) | string OR `{ code_execution: { model_id: 'MXX' } }` | freezeRow:1, headers pre-defined | tax-memo (M28-M31), lbo-focused, valuation-only, full-deal | +| `three_statement` | Multi-sheet IS/BS/CF model with balance check | `{ code_execution: { model_id: 'MXX' } }` | sheets_within:[Income Statement,Balance Sheet,Cash Flow], balance_check:true, freezeRow:1, freezeCol:'B' | lbo-focused | +| `dcf` | DCF valuation (Inputs/Cash Flows/Terminal Value/Sensitivity 2D sub-tabs) | `{ code_execution: { model_id: 'M07' } }` | sheets_within:[...4 tabs], freezeRow:1, formula_emphasis:'discount_rate' | valuation-only, full-deal | +| `comps` | 7-column comparables (Company/EV/EBITDA/EV/EBITDA/EV/Revenue/P/E/Source) | `{ code_execution: { model_id: 'MXX' } }` | freezeRow:1, conditional_format:'highlight_outliers' | valuation-only, full-deal | +| `lbo` | 5-sub-sheet LBO (Assumptions/Sources & Uses/Operating Model/Returns Analysis/Sensitivity) | `{ code_execution: { model_id: 'M21' } }` | sheets_within:[...5 tabs], freezeRow:1 | lbo-focused, full-deal | +| `sensitivity_2d` | 2D heatmap (e.g., WACC × terminal growth) | `{ aggregate: ['MXX'] }` OR similar | axis_x, axis_y, heatmap:true, freezeRow:1, freezeCol:'B' | lbo-focused, valuation-only, full-deal | +| `risk_register` | Risk table (Finding/Severity/Probability/Exposure $/Mitigation/Citation) | string (e.g., `'risk-aggregator-state.json'`) | freezeRow:1, conditional_format:'severity_color' | full-deal | +| `sources_index` | Sources/provenance index (7 cols incl. code_execution_id per v4.4 D24) | string (e.g., `'consolidated-footnotes.md'`) | freezeRow:1 | all 5 templates | +| `auto_per_model` | **Dynamic** — renderer iterates `code_executions` rows, creates one tab per `model_id` | `{ iterate: 'code_executions' }` | iteration:'per_code_execution_row', tab_naming:'{model_id}-{shortName}', **max_tabs:20 (safety cap)** | session-models fallback only | +| `model_index` | Model summary index (Model ID/Name/Category/Tab/Status/Duration) | string (e.g., `'invoked_models_summary'`) | freezeRow:1, headers pre-defined | session-models fallback | + +### Source shapes (3 forms) + +| Form | Example | Semantics | +|---|---|---| +| String (file/state key) | `'memo-state.json'`, `'consolidated-footnotes.md'`, `'invoked_models_summary'` | Renderer reads from the named state file or in-memory key | +| Object — `code_execution` | `{ code_execution: { model_id: 'M07' } }` | Renderer pulls the specified model's output from `code_executions` rows | +| Object — `aggregate` | `{ aggregate: ['M07','M08'] }` | Renderer aggregates outputs from multiple models | +| Object — `iterate` | `{ iterate: 'code_executions' }` | **Only for `auto_per_model` layout** — renderer iterates each row, generates one tab each | + +### Extending the catalog + +To add a new layout, edit `_layouts.js` and document it here. The renderer's openpyxl Python uses the layout spec to drive sheet construction. Layouts are pure data — no function fields. + +--- + +## §5 — `XLSX_TEMPLATE_BASE` reference + +Source: `_templateBase.js`. Templates import and use these as defaults: + +```javascript +import { XLSX_TEMPLATE_BASE } from './_templateBase.js'; +// ... +formulaWhitelist: XLSX_TEMPLATE_BASE.PRE_2019_FUNCTIONS, +citationDiscipline: XLSX_TEMPLATE_BASE.SOURCES_SHEET_SPEC, +cellColoring: XLSX_TEMPLATE_BASE.CELL_COLORING, +``` + +### `PRE_2019_FUNCTIONS` (40 Excel functions) + +Validated against Excel 2016+, Excel-Mac current, LibreOffice via Phase 0 Probe 3. Adding new functions requires re-running Probe 3 to verify they work across all targets. + +| Category | Functions | +|---|---| +| Aggregation | SUM, AVERAGE, MEDIAN, STDEV, VAR, MIN, MAX, SUMIF, SUMIFS, COUNTIF, COUNTIFS, SUMPRODUCT | +| Lookup/Reference | INDEX, MATCH, VLOOKUP, HLOOKUP, OFFSET | +| Financial | NPV, IRR, XNPV, XIRR, PMT, PV, FV, RATE | +| Logic | IF, IFS, AND, OR, NOT, IFERROR | +| Text | CONCATENATE, LEFT, RIGHT, MID, LEN, TEXT | +| Math | ROUND, CEILING, FLOOR | + +**Excluded** (intentionally — pre-2019 incompatibility): LET, LAMBDA, XLOOKUP, FILTER, SORT, SEQUENCE, UNIQUE. + +### `CELL_COLORING` (4 semantic colors) + +| Semantic | Fill | Font color | Meaning | +|---|---|---|---| +| `HARDCODED_INPUT` | `B4C7E7` (light blue) | `0000FF` (blue) | Hardcoded input — must have Sources sheet row mapping to citation tag | +| `FORMULA` | (none) | `000000` (black) | Formula cell — must NOT contain literal numbers | +| `CROSS_SHEET_LINK` | (none) | `008000` (green) | Cross-sheet reference | +| `OVERRIDE` | (none) | `FF0000` (red) | Manual override warning (operator caution) | + +### `SOURCES_SHEET_SPEC` (7-column structure) + +Every workbook must include a Sources sheet matching this schema: + +| Column | Purpose | +|---|---| +| Named Range | Workbook-level named range identifier | +| Sheet!Cell | A1-notation pointer to the cell using this source | +| Value Type | Numeric type / currency / etc. | +| Citation Tag | Marker linking to memo footnote | +| Source URL | Original URL where data was obtained | +| Fetched At | Timestamp | +| code_execution_id | v4.4 D24 provenance UUID for forensic lookup | + +**Constraint**: `requireMappingFor: 'all_BLUE_cells'` — every cell colored BLUE must have a corresponding Sources sheet row. + +### `AUDIT_CHECKS` (6 post-render checks) + +Each check returns `{ passed: bool, ...details }`. Aggregate `audit_results.status = PASS` iff ALL checks pass. + +| Check | Purpose | +|---|---| +| `no_excel_errors` | No `#REF!`, `#DIV/0!`, `#VALUE!`, `#NAME?`, `#NUM!`, `#N/A` in any cell | +| `no_hardcodes_in_formulas` | Formula cells contain no literal numbers (must reference inputs/named ranges) | +| `cross_sheet_refs` | Every cross-sheet reference resolves to a real cell | +| `balance_check` | 3-statement only: assets = L + E within 0.01% | +| `sources_sheet_complete` | Every BLUE cell has a Sources sheet row | +| `named_ranges_resolve` | Every named range target is non-empty | + +--- + +## §6 — Single-turn vs multi-turn (`phaseSplit`) + +**Decision rule**: single-turn = no `phaseSplit` field. Multi-turn = `phaseSplit` present. + +NO `type` field. The Zod schema validates `phaseSplit?` (optional); presence determines orchestration. + +### Single-turn + +- Use when estimated wall time ≤ 240s, sheet count ≤ 6, no specialty per-phase audit needed +- One bridge call generates the entire workbook +- Caller: `xlsxRenderer/index.js:209` sets `spec.callerCategory = 'xlsx_single_turn'` +- Example: `session-models` + +### Multi-turn + +- Use when wall time > 240s, sheet count > 6, OR high-risk sheets benefit from isolation (per PR #134 sensitivity isolation principle) +- Phase 1 + Phase 2 + Phase 3 required; Phase 4 + Phase 5 optional +- Per-phase `estimated_seconds` in [1, 1200]; sum should be ≤ 1200 (OVERALL_TIMEOUT_MS) +- Each phase declares `{ sheets: [...], label?, estimated_seconds? }` +- Caller: `multiTurnOrchestrator.js:97` sets `phaseSpec.callerCategory = 'xlsx_multi_turn'` +- Examples: `full-deal-workbook` (5 phases), `lbo-focused` (4 phases), `valuation-only` (4 phases), `tax-memo-workbook` (4 phases) + +### PR #134 sensitivity-isolation principle + +When one sheet dominates a phase's wall time (e.g., sensitivity tornado, LBO returns analysis, comparables fetching), isolate it in its own phase. Move smaller sheets to other phases. Phase 4 of `full-deal-workbook` was rebalanced this way: `[sensitivity, risk_register]` → `[sensitivity]` alone (PR #134). + +--- + +## §7 — When to extend (adding a new trigger field or layout) + +### Adding a new trigger field + +1. Ensure the caller of `selectTemplate(sessionContext)` populates the new field in `sessionContext` +2. Update §3 above to document the new field + its semantics +3. Templates can now reference it via `{ field: '', op: '...', value: '...' }` — no dispatcher edit needed (it passes the field through `ctx[clause.field]`) +4. Re-run validator: `python3 .claude/skills/xlsx-workbook-template-creator/scripts/validate-template.py ` — should pass without WARNING after README update + +### Adding a new layout + +1. Edit `_layouts.js`: add a new key in `LAYOUTS` with the layout spec (pure data — no functions) +2. Update §4 above to document: what the layout produces, source shape, constraints, used-by template +3. Update the renderer's openpyxl Python if the layout introduces new patterns (e.g., new conditional formatting style) +4. Re-run validator — should pass without WARNING after README update + +### Adding a new template + +Use the scaffolder: + +```bash +bash .claude/skills/xlsx-workbook-template-creator/scripts/create-template.sh +python3 .claude/skills/xlsx-workbook-template-creator/scripts/validate-template.py +bash .claude/skills/xlsx-workbook-template-creator/scripts/add-test-fixtures.sh +# After staging deploy: +bash .claude/skills/xlsx-workbook-template-creator/scripts/verify-observability.sh +``` + +Read `.claude/skills/xlsx-workbook-template-creator/SKILL.md` for the full workflow. + +### Hard-coded test assertion to update + +When adding a new template, `test/sdk/xlsx-renderer-integration.test.js:120` hard-codes "registry contains all 5 templates". Bump to `6` (or current N+1). The skill's `verification-checklist.md` flags this as a CRITICAL step. + +--- + +## Related references + +- **Prose sidecars (the markdown files in `prompts/xlsx-templates/`)** — see `super-legal-mcp-refactored/prompts/xlsx-templates/README.md` +- **Skill workflow + pitfalls** — `.claude/skills/xlsx-workbook-template-creator/SKILL.md` + `references/pitfalls-pr134-pr140.md` +- **Bridge observability (envelope_source, callerCategory propagation)** — `super-legal-mcp-refactored/docs/code-execution-enhancements/anthropic-sdk-best-practices-research.md` §13 +- **Operator runbook for STRUCTURED_OUTPUT_ENFORCEMENT flag** — `super-legal-mcp-refactored/docs/runbooks/envelope-decision-debug-playbook.md` +- **Feature flag #41 (XLSX_RENDERER) + #42 (STRUCTURED_OUTPUT_ENFORCEMENT)** — `super-legal-mcp-refactored/docs/feature-flags.md`