# 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<Record<PageRole, ResolvedPage[]>>;
  issues: RuntimeIssue[];
}

interface RenderPolicy {
  section: PageRole;
  enabled: boolean;
  required: boolean;
  visibleWhenEmpty: boolean;
}

interface BuiltSection<TRole extends PageRole = PageRole> {
  role: TRole;
  enabled: boolean;
  rendered: boolean;
  pages: Template['schemas'];
  templateInputPatch: Record<string, unknown>;
  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<string, unknown>;
}

interface AssembledTemplate {
  template: Template;
  templateInput: Record<string, unknown>;
  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.
