# Task P.4.2 Runtime Design ## Scope This document defines the revised architecture design for the next-generation CRM PDF runtime requested by [task-p.4.2.md](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/plans/task-p.4.2.md). This is architecture work only. - no production code changes - no template JSON changes - no database changes - no API changes This design reuses verified findings from: - [task-p.4-discovery-report.md](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/plans/task-p.4-discovery-report.md) - [task-p.4.1-verification-report.md](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/plans/task-p.4.1-verification-report.md) --- ## 1. Current Architecture Summary ### 1.1 Current runtime shape The current quotation PDF runtime is effectively: ```text Quotation Document Data -> Active Template Resolution -> Mapping Resolution -> Mutate Topic Page -> PDF Generator ``` The same underlying pipeline is used by: - document preview - PDF preview - PDF download - approved PDF generation ### 1.2 Current strengths The existing foundation already gives us: - DB-backed active template versioning - DB-backed placeholder mappings - shared quotation document data builder - shared PDF generation gateway - working topic pagination - working PDFMe table support - safe rollback through template version activation ### 1.3 Current architectural problem The main problem is not PDF generation itself. The problem is that rendering behavior depends on page order. Today the topic runtime assumes: ```ts schemas[1] ``` is always the topic/signature page. That means business logic currently depends on: - physical page indexes instead of: - logical document roles - logical sections This makes page insertion fragile and blocks optional section composition. --- ## 2. Proposed Architecture ### 2.1 Architecture principles The new runtime must be: - page-role driven - section-based - version compatible - renderer independent - extensible ### 2.2 Architectural shift The runtime should move from: ```text Template -> Mutate Page 2 -> Generate PDF ``` to: ```text Document Data -> Template Resolver -> Mapping Resolver -> Compatibility Adapter -> Page Resolver -> Render Policy Resolver -> Section Composer -> Template Assembler -> PDF Generator ``` ### 2.3 Main design idea The core design change is: - treat document content as logical sections - treat pages as render targets discovered by role - let render policy decide whether each section should render - let the assembler compose the final template from section outputs This means future changes are made by adding: - a new section role - a new section builder - a new marker rule - a new assembly rule instead of rewriting the whole runtime. --- ## 3. Section-Based Rendering Model ### 3.1 Section concept A `Document Section` represents logical document content, not fixed physical pages. Example sections: - `customer` - `product_items` - `topics` - `conditions` - `signature` - `attachments` - `appendix` - `warranty` - `cover` ### 3.2 Section output behavior A section may generate: - zero pages - one page - multiple pages This is important because: - product items may paginate to many pages - topics may paginate to many pages - optional sections may render nothing ### 3.3 Separation rule Each section builder must own only its own content domain. Examples: - Product Item rendering must not know topic rendering - Topic rendering must not know product rendering - Signature placement must not know product table layout internals --- ## 4. Optional Sections and Render Policy ### 4.1 Motivation The new requirement adds optional section support. That means the runtime must support skipping logical sections without requiring: - another template version - another runtime pipeline ### 4.2 Section requirement model Each section should be evaluated through an explicit render policy. Proposed contract: ```ts type PageRole = | 'customer' | 'product_items' | 'topics' | 'conditions' | 'signature' | 'attachments' | 'appendix' | 'warranty' | 'cover' | 'unknown'; interface RenderPolicy { section: PageRole; enabled: boolean; required: boolean; visibleWhenEmpty: boolean; } ``` ### 4.3 Policy meaning - `enabled` - whether the runtime should attempt to render the section - `required` - whether missing output should be treated as error - `visibleWhenEmpty` - whether an enabled section should still render a shell/placeholder when it has no content ### 4.4 Architectural examples Example policy set: ```text Customer: required Product Items: optional Topics: optional Conditions: optional Signature: required ``` ### 4.5 Policy source Policy source is intentionally undefined in this task. Future tasks may supply it from: - user selection - customer preference - template defaults - organization defaults The architecture only needs a clean slot for the policy, not a persistence/UI decision yet. ### 4.6 Important constraint Template versions should be used for: - structural differences - layout differences Template versions should not be used solely for: - enabling or disabling logical sections That responsibility belongs to render policy. --- ## 5. Runtime Pipeline ### 5.1 Proposed pipeline ```text Document Data -> Template Resolver -> Mapping Resolver -> Compatibility Adapter -> Page Resolver -> Render Policy Resolver -> Section Composer -> Customer Section Builder -> Product Item Engine -> Topic Engine -> Condition Engine -> Signature Resolver -> Template Assembler -> PDF Generator ``` ### 5.2 Stage descriptions #### Stage 1: Document Data Builds normalized business data: - quotation fields - customer fields - product items - topics - signatures - approval metadata Does not know: - page structure - template markers - physical page ordering #### Stage 2: Template Resolver Finds: - active template - active version - runtime `schemaJson` - template metadata Does not know: - business sections - page roles - render policy #### Stage 3: Mapping Resolver Finds: - placeholder mappings - table column mappings - defaults - format masks Does not know: - page placement - optionality rules #### Stage 4: Compatibility Adapter Normalizes legacy and future template models into one page-role-aware runtime model. Owns: - legacy page-role inference - explicit marker interpretation - marker validation - compatibility warnings #### Stage 5: Page Resolver Builds a page inventory from the normalized template. Owns: - page lookup by role - duplicate detection - fallback ordering - insertion anchors #### Stage 6: Render Policy Resolver Produces the effective policy for each section. Owns: - section enablement state - required/optional state - visible-when-empty behavior Does not own: - section rendering itself #### Stage 7: Section Composer Coordinates section builders in policy order. Owns: - builder orchestration - passing section inputs - collecting built sections - skipping disabled sections Does not own: - detailed rendering logic of each section #### Stage 8+: Section Builders Each builder produces section output only for its own section. Builders include: - Customer Section Builder - Product Item Engine - Topic Engine - Condition Engine - Signature Resolver #### Final Stage: Template Assembler Combines: - preserved template pages - generated section pages - section input patches into one final assembled template. #### Final Stage: PDF Generator Renders the assembled result through the existing PDFMe gateway. --- ## 6. Runtime Components ### 6.1 Template Resolver Responsibility: - resolve active template/version - return raw template payload Input: - organization id - document type - product type - file type Output: - `ResolvedTemplate` ### 6.2 Mapping Resolver Responsibility: - resolve version mappings and table columns Input: - `ResolvedTemplate` Output: - `ResolvedTemplateMappings` ### 6.3 Compatibility Adapter Responsibility: - convert raw runtime template into normalized page-role-aware model - handle legacy inference - expose compatibility issues Input: - `ResolvedTemplate` - `ResolvedTemplateMappings` Output: - `CompatibleTemplate` ### 6.4 Page Resolver Responsibility: - resolve page identities - build role-based lookup - determine insertion anchors Input: - `CompatibleTemplate` Output: - `ResolvedPages` ### 6.5 Render Policy Resolver Responsibility: - determine which sections should render - determine required/optional behavior - determine empty-state display rules Input: - document data - template metadata - default runtime policy Output: - `ResolvedRenderPolicies` ### 6.6 Section Composer Responsibility: - orchestrate section builders - pass shared context - collect built section outputs Input: - document data - resolved pages - resolved render policies Output: - `BuiltSections` ### 6.7 Customer Section Builder Responsibility: - build customer section only - preserve customer-page behavior Input: - customer data - target customer page Output: - `BuiltSection<'customer'>` ### 6.8 Product Item Engine Responsibility: - build product item pages only - paginate table - repeat headers - handle dynamic row height Input: - items - product-items base page - product section render policy Output: - `BuiltSection<'product_items'>` ### 6.9 Topic Engine Responsibility: - build topic pages only - paginate topics Input: - topics - topic base page - topic section render policy Output: - `BuiltSection<'topics'>` ### 6.10 Condition Engine Responsibility: - build condition/terms section only Input: - condition data or condition template markers - condition policy Output: - `BuiltSection<'conditions'>` ### 6.11 Signature Resolver Responsibility: - build signature section only - handle keep-together logic - last-page placement policy Input: - signatures - signature page - prior assembled content - signature policy Output: - `BuiltSection<'signature'>` ### 6.12 Template Assembler Responsibility: - combine preserved and generated pages - apply assembly order - merge input patches Input: - compatible template - built sections Output: - `AssembledTemplate` ### 6.13 PDF Render Gateway Responsibility: - font loading - plugin registration - final PDFMe rendering Input: - `AssembledTemplate` Output: - rendered PDF bytes --- ## 7. Page Marker Strategy ### 7.1 Goal The runtime must never depend on: ```ts schemas[1] ``` The runtime must resolve pages by logical markers and fallbacks. ### 7.2 Explicit marker strategy Future templates should include explicit role markers. Example: ```ts { name: '__page_role__product_items', type: 'text', content: 'product_items' } ``` or ```ts { name: '__page_role__topics', type: 'text', content: 'topics' } ``` ### 7.3 Legacy inference strategy Existing templates do not have explicit markers. Legacy inference rules should be centralized in the compatibility adapter. Examples: - page containing customer identity fields => `customer` - page containing `topic` + `data_topic` => `topics` - page containing `app1`/`app2`/`app3` and closing signature text => `signature` - future page containing `items_table` => `product_items` - future page containing dedicated condition markers => `conditions` ### 7.4 Fallback detection If no explicit marker or legacy inference succeeds: - mark page as `unknown` - record a runtime issue - optionally continue if page is non-required ### 7.5 Marker precedence Priority order: 1. explicit marker 2. legacy inference 3. fallback guess 4. unknown --- ## 8. Runtime Contracts ### 8.1 Core contracts ```ts type PageRole = | 'customer' | 'product_items' | 'topics' | 'conditions' | 'signature' | 'attachments' | 'appendix' | 'warranty' | 'cover' | 'unknown'; interface ResolvedTemplate { templateId: string; templateVersionId: string; templateName: string; version: string; schemaJson: Template; } interface ResolvedTemplateMappings { mappings: DocumentTemplateMappingWithColumns[]; } interface ResolvedPage { pageIndex: number; role: PageRole; strategy: 'explicit_marker' | 'legacy_inference' | 'fallback'; schema: Template['schemas'][number]; } interface ResolvedPages { all: ResolvedPage[]; byRole: Partial>; issues: RuntimeIssue[]; } interface RenderPolicy { section: PageRole; enabled: boolean; required: boolean; visibleWhenEmpty: boolean; } interface BuiltSection { role: TRole; enabled: boolean; rendered: boolean; pages: Template['schemas']; templateInputPatch: Record; issues: RuntimeIssue[]; } interface RuntimeIssue { code: | 'MISSING_MARKER' | 'DUPLICATE_MARKER' | 'INVALID_TEMPLATE' | 'MISSING_MAPPING' | 'EMPTY_OPTIONAL_SECTION' | 'EMPTY_REQUIRED_SECTION' | 'LEGACY_COMPAT_MODE'; severity: 'warning' | 'error'; message: string; details?: Record; } interface AssembledTemplate { template: Template; templateInput: Record; issues: RuntimeIssue[]; } ``` ### 8.2 Ownership boundaries - `ResolvedTemplate` belongs to template resolution - `ResolvedTemplateMappings` belongs to mapping resolution - `ResolvedPages` belongs to page resolution - `RenderPolicy` belongs to policy resolution - `BuiltSection` belongs to section builders - `AssembledTemplate` belongs to assembly only - `RuntimeIssue` is shared, accumulative diagnostic output --- ## 9. Section Composer Design ### 9.1 Purpose The section composer is a new first-class component introduced by the updated requirement. Its job is to coordinate section rendering independently from page index assumptions. ### 9.2 Responsibilities - evaluate effective render policy - call only enabled section builders - preserve required section order - collect built outputs - collect runtime issues ### 9.3 Example composition order Suggested default composition order: 1. `customer` 2. `product_items` 3. `topics` 4. `conditions` 5. `signature` 6. `attachments` 7. `appendix` This order is logical, not tied to page indexes. ### 9.4 Composer interface ```ts interface SectionComposerInput { documentData: QuotationDocumentData; resolvedPages: ResolvedPages; policies: RenderPolicy[]; } interface SectionComposerOutput { sections: BuiltSection[]; issues: RuntimeIssue[]; } ``` --- ## 10. Compatibility Strategy ### 10.1 Objective Support: - legacy templates - future marker-based templates without duplicating runtime logic. ### 10.2 Design rule Legacy support must be isolated in: - `Compatibility Adapter` Every component after that should consume normalized contracts only. ### 10.3 Why this matters Without this layer, optional sections and page-role logic would leak branching across: - topic engine - product item engine - assembler - signature logic That would make the runtime harder to extend and harder to verify. ### 10.4 Compatibility outcome Legacy templates stay valid because: - their pages can still be inferred by field patterns - their mappings remain unchanged - their active versions remain selectable Future templates become simpler because: - markers are explicit - sections are additive - optionality is policy-driven, not template-version-driven --- ## 11. Future Extensibility ### 11.1 Additive extension model A new document section should require only: 1. a `PageRole` 2. a section builder 3. a marker rule 4. an assembly rule ### 11.2 Supported future examples - drawing - specification - gallery - warranty - appendix - inspection report ### 11.3 Builder isolation rule Existing section builders must not need modification when a new section is added, unless a shared contract truly changes. That keeps the runtime additive instead of fragile. --- ## 12. Error Handling Strategy ### 12.1 Missing marker Behavior: - try explicit marker - try legacy inference - try fallback guess - otherwise emit `MISSING_MARKER` ### 12.2 Duplicate marker Behavior: - duplicate singleton section like `customer` or `signature` => error - duplicate repeatable section like `attachments` or `appendix` => warning only if ambiguous ### 12.3 Missing mappings Behavior: - if required mapped section cannot render => error - if optional section cannot render => warning and skip if policy allows ### 12.4 Empty optional section Behavior: - emit `EMPTY_OPTIONAL_SECTION` - skip rendering when `visibleWhenEmpty = false` - render placeholder shell when `visibleWhenEmpty = true` ### 12.5 Empty required section Behavior: - emit `EMPTY_REQUIRED_SECTION` - fail gracefully before final PDF generation if policy requires output ### 12.6 Invalid template Behavior: - stop assembly - return `INVALID_TEMPLATE` ### 12.7 Issue accumulation All stages append to `RuntimeIssue[]`. This supports: - preview diagnostics - audit tooling - rollout safety This remains consistent with ADR-0013 parity/audit discipline. --- ## 13. Migration Strategy ### 13.1 Migration goal Move from the current runtime to a section-based runtime without breaking: - preview - download - approved PDF - existing template versions ### 13.2 Constraints - no database migration - no API changes - no approved artifact rewrite ### 13.3 Safe migration path 1. introduce runtime contracts 2. introduce compatibility adapter 3. introduce page resolver 4. introduce render policy resolver 5. introduce section composer 6. refactor topic engine to consume resolved topic page 7. add product item engine 8. add assembler-driven final composition 9. publish new structural template versions only when needed ### 13.4 Approved PDF safety Already stored approved PDFs remain safe because retrieval uses stored artifact binaries, not live re-rendering. Only future generated documents will use the refactored runtime. --- ## 14. Sequence Diagrams ### 14.1 Current Runtime ```mermaid sequenceDiagram participant U as User/Route participant DS as Document Service participant TR as Template Resolver participant MR as Mapping Resolver participant TE as Topic Engine participant PG as PDF Generator U->>DS: Request preview/download DS->>DS: buildQuotationDocumentData() DS->>TR: resolveTemplateForDocument() DS->>MR: resolveTemplateMappings() DS->>DS: mapDocumentDataToTemplateInput() DS->>TE: buildPdfTopicTemplate() TE-->>DS: mutated template + topic inputs DS-->>PG: final template + templateInput PG-->>U: PDF bytes ``` ### 14.2 Proposed Runtime ```mermaid sequenceDiagram participant U as User/Route participant DD as Document Data participant TR as Template Resolver participant MR as Mapping Resolver participant CA as Compatibility Adapter participant PR as Page Resolver participant RP as Render Policy Resolver participant SC as Section Composer participant CB as Customer Builder participant PI as Product Item Engine participant TO as Topic Engine participant CO as Condition Engine participant SG as Signature Resolver participant TA as Template Assembler participant PG as PDF Generator U->>DD: Request preview/download DD-->>TR: documentData TR-->>MR: resolvedTemplate MR-->>CA: mappings + template CA-->>PR: compatibleTemplate PR-->>RP: resolvedPages RP-->>SC: render policies SC->>CB: build customer section SC->>PI: build product section SC->>TO: build topic section SC->>CO: build condition section SC->>SG: build signature section CB-->>SC: built customer section PI-->>SC: built product section TO-->>SC: built topic section CO-->>SC: built condition section SG-->>SC: built signature section SC-->>TA: built sections TA-->>PG: assembled template + input PG-->>U: PDF bytes ``` --- ## 15. Component Diagram ```mermaid classDiagram class TemplateResolver class MappingResolver class CompatibilityAdapter class PageResolver class RenderPolicyResolver class SectionComposer class CustomerSectionBuilder class ProductItemEngine class TopicEngine class ConditionEngine class SignatureResolver class TemplateAssembler class PdfRenderGateway TemplateResolver --> MappingResolver MappingResolver --> CompatibilityAdapter CompatibilityAdapter --> PageResolver PageResolver --> RenderPolicyResolver RenderPolicyResolver --> SectionComposer SectionComposer --> CustomerSectionBuilder SectionComposer --> ProductItemEngine SectionComposer --> TopicEngine SectionComposer --> ConditionEngine SectionComposer --> SignatureResolver SectionComposer --> TemplateAssembler TemplateAssembler --> PdfRenderGateway ``` --- ## 16. Risk Assessment ### 16.1 Main risks - legacy inference becomes too magical if not centralized - optional section policy becomes entangled with template versioning - section builders may start leaking logic into each other - assembler may become implicit if order rules are not declared clearly ### 16.2 Mitigations - keep all legacy branching inside compatibility adapter - keep all enable/disable logic inside render policy resolver - keep all orchestration inside section composer - keep all page order rules inside assembler - keep template versions for layout/structure, not section toggles --- ## 17. Implementation Roadmap ### Phase 1: Runtime contracts Deliverables: - `PageRole` - `RenderPolicy` - `BuiltSection` - `RuntimeIssue` - `AssembledTemplate` ### Phase 2: Compatibility adapter Deliverables: - explicit marker support - legacy inference support - normalized compatible template output ### Phase 3: Page resolver Deliverables: - role-based page inventory - duplicate/missing marker diagnostics ### Phase 4: Render policy resolver Deliverables: - effective section policy resolution - required/optional rules - visible-when-empty rules ### Phase 5: Section composer Deliverables: - builder orchestration - section collection - policy-driven skipping ### Phase 6: Product item engine Deliverables: - item table rendering - pagination - repeated headers ### Phase 7: Template upgrade Deliverables: - structural template versions for new layouts only ### Phase 8: Regression verification Deliverables: - preview/download/approved regression checks - old/new version parity checks Each phase must be independently testable. --- ## 18. Final Recommendation Task P.4.3 should implement a `section-based runtime` with `render policy` and `page-role resolution` as first-class architecture concepts. The most important architectural order is: 1. contracts 2. compatibility adapter 3. page resolver 4. render policy resolver 5. section composer 6. section builders 7. assembler This keeps the runtime: - backward compatible - optional-section capable - future-extensible - independent from page indexes and leaves minimal architectural ambiguity for implementation.