PRD - JBRK Legal OS Reporting Letter (solution on DocGen)¶
status: draft
date: 2026-06-06
solution: jbrk
consumes: docgen framework
release: releases/r2-framework-production-features
primary-source: ../../reference/JBRK_Legal_OS_Info_Guide.docx
What this is. A solution-level PRD for one document type — JBRK Legal OS's tax-reorganization reporting letter — expressed entirely as a configuration of the generic DocGen framework. The goal is a genericity proof: show that DocGen's R2 composition primitives (Text / Snippet / Risk Block / Field + templated render + version history + generation snapshot) can produce this real, regulated document, and flag anything that does not fit a primitive. Vocabulary throughout is DocGen's; JBRK's origin term is noted once where it first appears.
Primary source:
../../reference/JBRK_Legal_OS_Info_Guide.docx(Richard Kirby for Jon Gilbert, 2026-04-21). Framework references:../../reference/domain-model.md([D-11]/[D-12]/[D-15]),../../releases/r2-framework-production-features/r2-prd.md([P2-composition]),../../explanation/phoenix-uc2-analysis.md(gaps G1/G2/G3).
TL;DR [JB-tldr]¶
JBRK Legal OS is a real, working ~3,000-line engine that assembles Canadian tax-reorganization reporting letters from a structured fact graph using pre-written, lawyer-approved templates. Its governing principle: AI extracts facts; humans write legal analysis; the engine delivers it deterministically — every word in Sections II and III comes from templates the lawyers wrote and approved. No LLM ever drafts the body.
This PRD recasts that engine as a DocGen solution. The four JBRK layers map onto DocGen R2 primitives with no new vocabulary:
| JBRK layer (origin term) | DocGen primitive | Role in the letter |
|---|---|---|
| Atom | Text (structured: heading / narrative / bullets, Jinja, context-independent {% if %}) |
one transaction step → Section II |
| Molecule | Snippet (ordered snippet_items of Texts + Risk Blocks, phase_order, entity_roles) |
a recognized transaction pattern |
| Risk Block | Risk Block (approval-gated status draft→approved) |
one statutory risk → Section III |
| Variable | Field (+ computed values via G2 = JBRK's tax_math) |
the flat dict the units interpolate |
The output is one document with three Sections (I background facts, II transaction steps, III risk commentary), where II and III are assembled deterministically by collating units across all bound Snippets and numbering steps with a single running counter.
Bottom line (see TDD [JB-T-coverage]): every JBRK need maps to a DocGen primitive once R2's three forcing-function gaps land — G1 (structured/iterable render data), G2 (computed fields = tax_math), and the cross-snippet grouped-by-type assembly + step_num running counter ([T2-render]). The grouped assembly is real new render behavior, not just configuration; it is the one place JBRK forces framework work beyond "fill in the catalog." Flagged in [JB-T-misfits].
Use case [JB-feat-uc]¶
Who¶
- Jon Gilbert — supervising tax lawyer at JBRK (Tomorrow Law Professional Corporation). Author of all legal content: the transaction-step Texts (Section II), the Risk Blocks (Section III), and the Snippet definitions that compose them. He owns the substance; Claude helps with template format.
- Richard Kirby — infrastructure/plumbing: the fact-extraction adapter, the data graph, and the
tax_mathcomputations (→ DocGen computed Fields, G2). Reviews Jon's templates for render correctness. - The reviewing lawyer (Jon and/or Richard) — operates the human approval gate: signs off every Risk Block (
statusdraft→approved) and reviews the assembled letter (Document.state) before any execution documents are generated.
What¶
A reporting letter — the analytical core of every tax-reorg file:
| Section | Content | Produced by |
|---|---|---|
| Section I | Background facts (entities, shareholdings, asset FMVs, the transaction context) | facts gathered into the Field dict; narrative is templated or a tightly-scoped LLM summary of approved facts |
| Section II | Transaction steps + tax consequences — every step across all transaction patterns, in execution order, sequentially numbered | deterministic: all Text units across all bound Snippets, grouped, Jinja-rendered against Fields |
| Section III | Strategic risk commentary — every statutory-risk analysis across all patterns | deterministic: all approved Risk Block units across all bound Snippets, grouped |
Transaction patterns (the Snippets we ship first)¶
The engine handles three patterns today and is designed to scale to ~15. The reporting letter for a file binds one or more of these (a real reorg is often a freeze then a pipeline):
| Pattern (JBRK molecule) | DocGen Snippet key |
Typical phase_order |
Composition (Texts → Risk Blocks) |
|---|---|---|---|
| Post-mortem pipeline | post_mortem_pipeline |
10 | Incorporate Newco + s.85(1) Transfer + Tax-Funding Dividend + Surplus Extraction → s.84(1), s.84(2), s.55(2)/Part IV, s.164(6), GAAR |
| Inter vivos pipeline | inter_vivos_pipeline |
10 | similar step chain, living transferor → overlapping risk set |
| Estate freeze | estate_freeze |
0 | freeze exchange + Newco subscribe + (optional) crystallization → s.86, s.85, valuation/GAAR risks |
phase_order sorts the patterns when several apply to one letter (freeze 0 → crystallization 1 → pipeline 10), so Section II reads in execution order even across patterns.
The human approval gate (non-negotiable)¶
This is the reason the document exists in DocGen and not as free LLM prose. Two layers:
- Per-unit gate. A Risk Block renders in production only when
status='approved'([P2-feat-2]). Draft blocks are refused or rendered visibly tagged DRAFT. The lawyer authors a draft, reviews, and promotes it once — institutional knowledge captured as a reusable, approved unit. - Document-level gate. The assembled letter sits at
Document.state='under-review'for lawyer sign-off; only after sign-off (finalized) may downstream execution documents (T2057 elections, transfer agreements, resolutions) be generated. The gate is between assembly and execution — DocGen's render produces the letter; execution-doc generation is a separate, gated step.
Inputs [JB-feat-inputs]¶
The relational fact graph (ingestion is faked/mocked for this solution)¶
JBRK extracts an accountant's instruction-letter PDF into a relational graph. For this DocGen solution, ingestion is mocked — a fixture graph stands in for the extractor (the real PDF→graph extractor is JBRK solution code, not framework, and lands later). The graph shape:
- Corporations — tax attributes: name, jurisdiction, fiscal year end, CCPC status, retained earnings, CDA / GRIP / NERDTOH / ERDTOH balances, total FMV, total PUC.
- Individuals — roles (SHAREHOLDER, EXECUTOR, TRUSTEE, BENEFICIARY, DIRECTOR, SUCCESSOR),
spouse_id,children_ids. - CorporateAssets — FMV, ACB, active-business-asset flag.
- Shareholdings (edges) — share class, quantity, PUC, ACB, FMV, voting/participating/redeemable rights.
- TransactionSteps (edges) —
step_type(INCORPORATE, TRANSFER_SHARES, EXCHANGE_SHARES, PAY_DIVIDEND, …), entity refs, subject property, consideration, optional elected amount.
The Field dict (~60 values) and tax_math¶
An adapter walks the graph and flattens it to a Field dict the templates interpolate ({{ Opco_name }}, {{ Extraction_amount }}, {{ capital_gain }}, {{ accounting_firm }}, …). Some values are computed, not stored — capital gains, deemed dividends, safe-income figures — produced by JBRK's tax_math/ pure functions (one module per ITA provision, each unit-tested). In DocGen terms these are computed Fields (G2): solution-registered pure functions whose outputs merge into the render scope. The framework owns the merge plumbing; JBRK owns the formulas.
"Every word approved" governance¶
Facts may be AI-extracted (Section I inputs). Analysis is never AI-authored. Every Text and every Risk Block is lawyer-written, reviewed, and (for Risk Blocks) explicitly approved before it can appear in a produced letter. The approval gate + version history + generation snapshot together make this enforceable and auditable, not aspirational.
Multi-Snippet (phase-ordered) documents¶
One letter can bind multiple Snippets (e.g. an estate freeze followed by a post-mortem pipeline). document_snippets binds each pattern to the document and maps roles (opco, transferor, …) to concrete graph entities for this file (entity_bindings_json). Assembly orders all units by (phase_order, ordinal).
Success criteria [JB-feat-success]¶
| # | Criterion | How verified |
|---|---|---|
[JB-success-1] |
Deterministic II/III from approved units only. Re-rendering the same document from the same approved units + Field values yields byte-identical Section II and III. No LLM in the II/III path. | re-render check; generation snapshot reproduces byte-for-byte (templated path) |
[JB-success-2] |
Correct multi-Snippet assembly + sequential step numbering. Binding freeze (phase_order=0) + pipeline (phase_order=10) yields Section II with all steps in (phase_order, ordinal) order, numbered 1..N by a single running counter across both patterns; Section III lists all approved Risk Blocks in the same order. |
worked example ([JB-T-example]); assert step_num runs 1..N across snippets, not per-snippet |
[JB-success-3] |
Per-unit approval enforced. A Risk Block with status='draft' does not appear in a finalized letter (refused or DRAFT-tagged). |
render with a draft block present → assert excluded/tagged |
[JB-success-4] |
Lawyer sign-off workflow. The letter cannot drive execution-document generation until Document.state='finalized'; the audit spine (version history + generation snapshot) can answer "show me the exact approved units + facts that produced the letter we filed." |
state-gate check; generation-snapshot manifest pins every consumed unit version |
[JB-success-5] |
Context-independent Texts. The same s85_transfer Text renders correctly in a pipeline and a freeze, with variants driven only by {% if %} against the Field dict — never hardcoded. |
render s85_transfer under two Snippets; diff is conditional-driven only |
[JB-success-6] |
Scales additively. Adding a 4th transaction pattern (e.g. s.87 amalgamation) is "author Texts + Risk Blocks + one Snippet definition" — no framework change. | add a fixture Snippet; no engine edits |
Out of scope (this solution, this release)¶
- Real PDF→graph ingestion / extraction prompts — JBRK solution code, mocked here.
- Execution documents (T2057, transfer agreements, resolutions) and closing books — downstream of the gate; future.
- Error-detection rules that catch accountant mistakes in instruction letters — JBRK solution logic; overlaps DocGen's verify/precondition (G3) but not built here.
Cross-references¶
- TDD (the configuration + genericity proof):
./tdd.md - Primary source:
../../reference/JBRK_Legal_OS_Info_Guide.docx - DocGen R2 PRD:
../../releases/r2-framework-production-features/r2-prd.md([P2-composition]) - DocGen R2 TDD:
../../releases/r2-framework-production-features/r2-tdd.md([T2-render]assembly modes) - DocGen domain model:
../../reference/domain-model.md([D-11],[D-12]walkthroughs 3+4,[D-15]Fields) - Gap analysis:
../../explanation/phoenix-uc2-analysis.md(G1/G2/G3)