PRD + inventor design: submission generator v2 — sectioned composition, swappable base templates, in-app editing, building blocks #141

Open
opened 2026-05-26 16:27:02 +00:00 by mAi · 6 comments
Collaborator

Why

m (2026-05-26 18:26): "We still dont have user choice for our submission generator? Bro... we should do our own prd for the submission generator with an inventor. I want to have different sections, selectable 'base templates', editability of content, building blocks etc."

Current state is too thin:

  • One Word .docx template per submission_code (fetched from mWorkRepo).
  • Variable substitution via submission_vars.go.
  • The lawyer's only choice is which submission_code — after that the document is fixed-shape with substituted vars.
  • Editing happens in Word after download — paliad has no say.

What m wants — a v2 spec authored by an inventor before any code work starts:

Headline requirements (m's words mapped to design surface)

  1. Different sections — a submission is composed of sections (Rubrum / Sachverhalt / Rechtliche Würdigung / Anträge / Beweisangebote / Anlagen / Briefkopf / Signaturblock — names TBD by inventor). Each section is independently authored, ordered, and editable.
  2. Selectable base templates — the lawyer picks the BASE (e.g. firm letterhead vs neutral skeleton vs UPC-formal vs LG-Düsseldorf-style). The base sets fonts, margins, headings, the standard sections — but lawyer can swap.
  3. Editability of content — within paliad the lawyer can edit section text (free prose), not just fill variables. Edits persist on the draft (paliad.submission_drafts) and feed back into the rendered .docx.
  4. Building blocks — reusable chunks (e.g. Standardformulierung Anträge auf einstweilige Verfügung, Auslandszustellung Beweisangebot, Verspätungseinrede Boilerplate). Catalog of building blocks selectable per section, modifiable per use.

Open design surface for the inventor to resolve

  • Data model: is a section a row in submission_drafts.sections[] (jsonb)? A separate submission_sections table? How do building blocks relate to sections (FK / copy-on-insert / live-reference)?
  • Base templates: where do they live (mWorkRepo per-base directories? a submission_bases table?)? How is the base swapped after content is authored — do edits survive a base change?
  • Building blocks library: where authored (admin UI? mWorkRepo? per-firm vs per-user)? Versioning? Variables inside building blocks?
  • Render pipeline: how does v2 produce the final .docx? Compose-from-sections in Go via OOXML manipulation? Server-side LibreOffice? Pre-rendered base + section-injection?
  • Editing UX: rich-text editor in-browser (e.g. Tiptap, ProseMirror, custom)? Markdown with WYSIWYG preview? Per-section flat text + variables?
  • Variable bag interplay: existing {{party.*}} / {{court.*}} / {{lawyers.*}} (latter from #139) — do they stay placeholder-based or get bound at compose time per section?
  • Migration / backward-compat: existing submission_drafts (7 live rows on prod) — how do they transition? Per-code default sections to seed v2 drafts?
  • Multi-language: the base + sections + building blocks all need DE + EN variants in line with the language picker.
  • Out-of-scope (for inventor to call out): real-time collaborative editing, comment threads, version history beyond submission_drafts.updated_at.

Inventor deliverables

  1. A design doc at docs/design-submission-generator-v2-2026-05-26.md with the full PRD: scope, data model, slice plan (A → ... → Z), risk assessment, migration path.
  2. Each slice independently shippable (smallest-viable-step convention from prior #93 / #124 work).
  3. A concrete recommendation on each open design question — defaults the inventor proposes, with reasoning. m reviews + corrects.
  4. Inventor reports 'DESIGN READY FOR REVIEW' when the doc is committed. Inventor MUST NOT shift to coder. Head gates the inventor→coder transition per the standard rule.

Hard rules

  • NO AskUserQuestion — escalate via mai instruct head for real ambiguity. Open design questions go into the doc as 'recommended default + alternatives' for m to pick.
  • English identifiers in any new package; DE + EN labels in user-facing copy.
  • Design must respect today's submission_drafts.audit_log and the {{rule.X}} placeholder alias contract (never break Word templates already in mWorkRepo).
  • No premature implementation. This is design only.

Acceptance for this issue

  1. Inventor commits the design doc on its own branch.
  2. Doc covers all four headline requirements with concrete data-model + UX + render-pipeline proposals.
  3. Each open design question has a recommended default + at least one alternative.
  4. Slice plan present, smallest slice shippable independently.
  5. Inventor reports DESIGN READY FOR REVIEW. Head surfaces to m for greenlight.
## Why m (2026-05-26 18:26): "We still dont have user choice for our submission generator? Bro... we should do our own prd for the submission generator with an inventor. I want to have different sections, selectable 'base templates', editability of content, building blocks etc." Current state is too thin: - One Word .docx template per submission_code (fetched from mWorkRepo). - Variable substitution via `submission_vars.go`. - The lawyer's only choice is *which submission_code* — after that the document is fixed-shape with substituted vars. - Editing happens in Word after download — paliad has no say. What m wants — a v2 spec authored by an inventor before any code work starts: ### Headline requirements (m's words mapped to design surface) 1. **Different sections** — a submission is composed of sections (Rubrum / Sachverhalt / Rechtliche Würdigung / Anträge / Beweisangebote / Anlagen / Briefkopf / Signaturblock — names TBD by inventor). Each section is independently authored, ordered, and editable. 2. **Selectable base templates** — the lawyer picks the BASE (e.g. firm letterhead vs neutral skeleton vs UPC-formal vs LG-Düsseldorf-style). The base sets fonts, margins, headings, the standard sections — but lawyer can swap. 3. **Editability of content** — within paliad the lawyer can edit section text (free prose), not just fill variables. Edits persist on the draft (`paliad.submission_drafts`) and feed back into the rendered .docx. 4. **Building blocks** — reusable chunks (e.g. *Standardformulierung Anträge auf einstweilige Verfügung*, *Auslandszustellung Beweisangebot*, *Verspätungseinrede Boilerplate*). Catalog of building blocks selectable per section, modifiable per use. ### Open design surface for the inventor to resolve - **Data model**: is a section a row in `submission_drafts.sections[]` (jsonb)? A separate `submission_sections` table? How do building blocks relate to sections (FK / copy-on-insert / live-reference)? - **Base templates**: where do they live (`mWorkRepo` per-base directories? a `submission_bases` table?)? How is the base swapped after content is authored — do edits survive a base change? - **Building blocks library**: where authored (admin UI? mWorkRepo? per-firm vs per-user)? Versioning? Variables inside building blocks? - **Render pipeline**: how does v2 produce the final .docx? Compose-from-sections in Go via OOXML manipulation? Server-side LibreOffice? Pre-rendered base + section-injection? - **Editing UX**: rich-text editor in-browser (e.g. Tiptap, ProseMirror, custom)? Markdown with WYSIWYG preview? Per-section flat text + variables? - **Variable bag interplay**: existing `{{party.*}}` / `{{court.*}}` / `{{lawyers.*}}` (latter from #139) — do they stay placeholder-based or get bound at compose time per section? - **Migration / backward-compat**: existing `submission_drafts` (7 live rows on prod) — how do they transition? Per-code default sections to seed v2 drafts? - **Multi-language**: the base + sections + building blocks all need DE + EN variants in line with the language picker. - **Out-of-scope** (for inventor to call out): real-time collaborative editing, comment threads, version history beyond `submission_drafts.updated_at`. ### Inventor deliverables 1. A design doc at `docs/design-submission-generator-v2-2026-05-26.md` with the full PRD: scope, data model, slice plan (A → ... → Z), risk assessment, migration path. 2. Each slice independently shippable (smallest-viable-step convention from prior #93 / #124 work). 3. A concrete recommendation on each open design question — defaults the inventor proposes, with reasoning. m reviews + corrects. 4. Inventor reports 'DESIGN READY FOR REVIEW' when the doc is committed. **Inventor MUST NOT shift to coder.** Head gates the inventor→coder transition per the standard rule. ### Hard rules - NO AskUserQuestion — escalate via `mai instruct head` for real ambiguity. Open design questions go into the doc as 'recommended default + alternatives' for m to pick. - English identifiers in any new package; DE + EN labels in user-facing copy. - Design must respect today's submission_drafts.audit_log and the `{{rule.X}}` placeholder alias contract (never break Word templates already in mWorkRepo). - No premature implementation. This is design only. ## Acceptance for this issue 1. Inventor commits the design doc on its own branch. 2. Doc covers all four headline requirements with concrete data-model + UX + render-pipeline proposals. 3. Each open design question has a recommended default + at least one alternative. 4. Slice plan present, smallest slice shippable independently. 5. Inventor reports DESIGN READY FOR REVIEW. Head surfaces to m for greenlight.
mAi self-assigned this 2026-05-26 16:27:02 +00:00
Author
Collaborator

Slice A shipped on mai/cronus/coder-composer-slice-a @ e2969fc

Submission generator v2 (Composer) Slice A per the design at docs/design-submission-generator-v2-2026-05-26.md §12.

What landed:

  • 3 migrations (146/147/148): submission_bases catalog + submission_drafts.base_id + composer_meta columns + submission_sections per-draft rows. Seed rows for hlc-letterhead and neutral bases with 10-section defaults each.
  • BaseService (read-only) + SectionService (read + seed); transactional draft create that seeds base_id + 10 section rows.
  • GET /api/submission-bases catalog endpoint; PATCH endpoints accept base_id.
  • submissionDraftView gains base_id, composer_meta, sections.
  • Frontend: sidebar base picker + read-only section list pane above the preview. Hidden when catalog empty or no sections seeded.
  • Tests: unit (TestFamilyOfCode, TestBaseSectionSpec_*) + live integration (TestComposerSeedFlow, TEST_DATABASE_URL-gated) covering the seed flow end-to-end.

Hard constraints honoured:

  • NO behavior change to the .docx render path — v1 export still works exactly as today.
  • NO auto-upgrade of existing 11 drafts (deferred to Slice C per head's brief).
  • Migrations purely additive; v1 fallback render path stays compiled in (gate: base_id IS NULL OR no sections rows).
  • {{rule.X}} aliases preserved (unchanged from v1).

Build hygiene: go build/vet/test -short clean; bun run build clean (2900 i18n keys, data-i18n scan clean).

Migration slot coordination: Live DB at v106; main has migs through 145. Claimed 146/147/148. Dry-run dependent-failure for 146-148 matches the existing 130/131 pattern (those also depend on 119's submission_drafts which the live DB hasn't applied yet). Cumulative apply at Dokploy boot resolves it.

Status: SLICE A COMPLETE — ready for merge. Inventor parked, head dispatches Slice B.

## Slice A shipped on `mai/cronus/coder-composer-slice-a` @ e2969fc Submission generator v2 (Composer) Slice A per the design at `docs/design-submission-generator-v2-2026-05-26.md` §12. **What landed:** - 3 migrations (146/147/148): `submission_bases` catalog + `submission_drafts.base_id` + `composer_meta` columns + `submission_sections` per-draft rows. Seed rows for `hlc-letterhead` and `neutral` bases with 10-section defaults each. - BaseService (read-only) + SectionService (read + seed); transactional draft create that seeds base_id + 10 section rows. - `GET /api/submission-bases` catalog endpoint; PATCH endpoints accept `base_id`. - `submissionDraftView` gains `base_id`, `composer_meta`, `sections`. - Frontend: sidebar base picker + read-only section list pane above the preview. Hidden when catalog empty or no sections seeded. - Tests: unit (`TestFamilyOfCode`, `TestBaseSectionSpec_*`) + live integration (`TestComposerSeedFlow`, TEST_DATABASE_URL-gated) covering the seed flow end-to-end. **Hard constraints honoured:** - NO behavior change to the .docx render path — v1 export still works exactly as today. - NO auto-upgrade of existing 11 drafts (deferred to Slice C per head's brief). - Migrations purely additive; v1 fallback render path stays compiled in (gate: `base_id IS NULL OR no sections rows`). - `{{rule.X}}` aliases preserved (unchanged from v1). **Build hygiene:** go build/vet/test -short clean; bun run build clean (2900 i18n keys, data-i18n scan clean). **Migration slot coordination:** Live DB at v106; main has migs through 145. Claimed 146/147/148. Dry-run dependent-failure for 146-148 matches the existing 130/131 pattern (those also depend on 119's `submission_drafts` which the live DB hasn't applied yet). Cumulative apply at Dokploy boot resolves it. **Status:** SLICE A COMPLETE — ready for merge. Inventor parked, head dispatches Slice B.
Author
Collaborator

Slice B shipped on mai/cronus/coder-composer-slice-b @ f963b0d

The "Composer actually works" milestone — editable prose sections + anchor-spliced render + MD→OOXML walker for paragraphs.

Backend (~1200 LoC + tests):

  • submission_md.go — Markdown → OOXML walker (paragraphs + B/I + nested formatting + CRLF + XML escape). 12 unit tests.
  • submission_compose.go — anchor-spliced render pipeline. ConvertDotmToDocx pre-pass → extract document.xml → render each section's content_md → splice via {{#section:KEY}}/{{/section:KEY}} anchors → strip excluded → append unanchored before sectPr → repack → v1 placeholder pass. 6 unit tests covering anchor mode, append mode, excluded drop, placeholder resolution, lang column pick, order_index ASC.
  • submission_section_service.goSectionPatch + Update method, 6 optional fields (content_md_de/en, included, label_de/en, order_index).
  • submission_sections.goPATCH /api/submission-drafts/{draft_id}/sections/{section_id}. Owner-scoped via SubmissionDraftService.Get; section-belongs-to-draft cross-check.
  • submission_drafts.goexportSubmissionDraft branches on draft.BaseID. Composer pipeline when set + base + bytes + sections all resolve; else v1 fallback. Audit metadata gains composer: true + base_id.
  • files.gofetchComposerBaseBytes + composerBaseSlugMap reuse the existing Gitea proxy cache.

Frontend (~400 LoC + ~80 LoC CSS):

  • ContentEditable per section with per-section B/I toolbar. 500ms-debounced autosave. domToMarkdown walks the contentEditable's DOM tree back to Markdown source-of-truth.
  • Per-section Hide/Include toggle. flushSectionAutosave on blur prevents unsynced edits.
  • Avoids focus-stealing during typing — autosave-driven updates splice into state.view.sections in-place; full repaint only on structural changes.

Templates regenerated on Gitea (mAi-uploaded):

  • _skeleton.docx — anchor-only body. New blob: ac0cdeaf…
  • _firm-skeleton.docx — anchor-only body, sectPr preserved (firm header/footer rIds intact). New blob: f1e9a9fb…
  • gen-skeleton-submission-template gains -anchors flag (default true) for future regens.

Build hygiene: go build/vet/test -short ./internal/... ./cmd/... all clean; bun run build clean (2900 i18n keys).

Hard rules honoured:

  • NO behavior change for pre-Composer drafts (base_id NULL → v1 path stays compiled in).
  • NO new migrations (sections shape already in place from Slice A).
  • Q2 ratification preserved (no building_block_id lineage — Slice C).
  • Q10 ratification preserved (caption/letterhead/signature are regular prose sections seeded from base spec; editable).
  • {{rule.X}} aliases preserved (v1 renderer pass unchanged inside composer).

Status: SLICE B COMPLETE — ready for merge. Head dispatches Slice C (building blocks).

## Slice B shipped on `mai/cronus/coder-composer-slice-b` @ f963b0d The "Composer actually works" milestone — editable prose sections + anchor-spliced render + MD→OOXML walker for paragraphs. **Backend (~1200 LoC + tests):** - `submission_md.go` — Markdown → OOXML walker (paragraphs + B/I + nested formatting + CRLF + XML escape). 12 unit tests. - `submission_compose.go` — anchor-spliced render pipeline. ConvertDotmToDocx pre-pass → extract document.xml → render each section's content_md → splice via `{{#section:KEY}}/{{/section:KEY}}` anchors → strip excluded → append unanchored before sectPr → repack → v1 placeholder pass. 6 unit tests covering anchor mode, append mode, excluded drop, placeholder resolution, lang column pick, order_index ASC. - `submission_section_service.go` — `SectionPatch` + `Update` method, 6 optional fields (`content_md_de/en`, `included`, `label_de/en`, `order_index`). - `submission_sections.go` — `PATCH /api/submission-drafts/{draft_id}/sections/{section_id}`. Owner-scoped via SubmissionDraftService.Get; section-belongs-to-draft cross-check. - `submission_drafts.go` — `exportSubmissionDraft` branches on draft.BaseID. Composer pipeline when set + base + bytes + sections all resolve; else v1 fallback. Audit metadata gains `composer: true` + `base_id`. - `files.go` — `fetchComposerBaseBytes` + `composerBaseSlugMap` reuse the existing Gitea proxy cache. **Frontend (~400 LoC + ~80 LoC CSS):** - ContentEditable per section with per-section B/I toolbar. 500ms-debounced autosave. `domToMarkdown` walks the contentEditable's DOM tree back to Markdown source-of-truth. - Per-section Hide/Include toggle. flushSectionAutosave on blur prevents unsynced edits. - Avoids focus-stealing during typing — autosave-driven updates splice into `state.view.sections` in-place; full repaint only on structural changes. **Templates regenerated on Gitea (mAi-uploaded):** - `_skeleton.docx` — anchor-only body. New blob: `ac0cdeaf…` - `_firm-skeleton.docx` — anchor-only body, sectPr preserved (firm header/footer rIds intact). New blob: `f1e9a9fb…` - `gen-skeleton-submission-template` gains `-anchors` flag (default true) for future regens. **Build hygiene:** go build/vet/test -short ./internal/... ./cmd/... all clean; bun run build clean (2900 i18n keys). **Hard rules honoured:** - NO behavior change for pre-Composer drafts (base_id NULL → v1 path stays compiled in). - NO new migrations (sections shape already in place from Slice A). - Q2 ratification preserved (no building_block_id lineage — Slice C). - Q10 ratification preserved (caption/letterhead/signature are regular prose sections seeded from base spec; editable). - `{{rule.X}}` aliases preserved (v1 renderer pass unchanged inside composer). **Status:** SLICE B COMPLETE — ready for merge. Head dispatches Slice C (building blocks).
Author
Collaborator

Slice C shipped on mai/cronus/coder-composer-slice-c @ ee98db9

Building blocks library + section picker per design §8 / §4.4 and Q2/Q9 ratifications.

Schema (mig 149):

  • submission_building_blocks — library catalog (slug, firm, section_key, proceeding_family, title/desc/content_md per lang, author, visibility, is_published, soft-delete). RLS coarse-grained SELECT; tier predicate applied in Go.
  • submission_building_block_admin_versions — append-only history (retention=20); admin-side only, NOT referenced from sections per Q2.

Backend:

  • BuildingBlockService (~510 LoC) — ListVisible (tier predicate), ListAllForAdmin, Create/Update/SoftDelete/RestoreVersion (all transactional w/ retention-20 GC), InsertIntoSection (Q2's plain-text paste — no lineage stamped).
  • 9 handlers: lawyer-facing picker (GET /api/submission-building-blocks, POST .../insert-into/{section_id}) + admin editor under /api/admin/submission-building-blocks/* and /admin/submission-building-blocks page.

Frontend:

  • New /admin/submission-building-blocks admin shell + ~370 LoC client (3-pane: list / form / version log).
  • Per-section + Baustein button on the Composer editor toolbar opens a modal with 200ms-debounced search; click a hit → POST insert-into → section content updated in place.
  • 240 LoC CSS for modal + picker rows (tier-colored visibility chips) + admin 3-pane grid.

Tests: 3 pure-unit tests covering visibility constants + paste-content append behaviour.

Build hygiene: go build/vet/test -short clean; bun run build clean (2906 i18n keys).

Hard rules honoured:

  • Q2 paste-only semantic (no lineage stamped on sections).
  • Q9 4-tier visibility (private/team/firm/global).
  • NO behavior change for pre-Composer drafts.
  • {{rule.X}} aliases preserved (block content flows through v1 placeholder pass on export).

Status: SLICE C COMPLETE — ready for merge. Head dispatches Slice D (rich-prose extensions to MD→OOXML walker).

## Slice C shipped on `mai/cronus/coder-composer-slice-c` @ ee98db9 Building blocks library + section picker per design §8 / §4.4 and Q2/Q9 ratifications. **Schema (mig 149):** - `submission_building_blocks` — library catalog (slug, firm, section_key, proceeding_family, title/desc/content_md per lang, author, visibility, is_published, soft-delete). RLS coarse-grained SELECT; tier predicate applied in Go. - `submission_building_block_admin_versions` — append-only history (retention=20); admin-side only, NOT referenced from sections per Q2. **Backend:** - `BuildingBlockService` (~510 LoC) — `ListVisible` (tier predicate), `ListAllForAdmin`, `Create`/`Update`/`SoftDelete`/`RestoreVersion` (all transactional w/ retention-20 GC), `InsertIntoSection` (Q2's plain-text paste — no lineage stamped). - 9 handlers: lawyer-facing picker (`GET /api/submission-building-blocks`, `POST .../insert-into/{section_id}`) + admin editor under `/api/admin/submission-building-blocks/*` and `/admin/submission-building-blocks` page. **Frontend:** - New `/admin/submission-building-blocks` admin shell + ~370 LoC client (3-pane: list / form / version log). - Per-section `+ Baustein` button on the Composer editor toolbar opens a modal with 200ms-debounced search; click a hit → POST insert-into → section content updated in place. - 240 LoC CSS for modal + picker rows (tier-colored visibility chips) + admin 3-pane grid. **Tests:** 3 pure-unit tests covering visibility constants + paste-content append behaviour. **Build hygiene:** go build/vet/test -short clean; bun run build clean (2906 i18n keys). **Hard rules honoured:** - Q2 paste-only semantic (no lineage stamped on sections). - Q9 4-tier visibility (private/team/firm/global). - NO behavior change for pre-Composer drafts. - `{{rule.X}}` aliases preserved (block content flows through v1 placeholder pass on export). **Status:** SLICE C COMPLETE — ready for merge. Head dispatches Slice D (rich-prose extensions to MD→OOXML walker).
Author
Collaborator

Slice D shipped on mai/cronus/coder-composer-slice-d @ 6778497

Rich-prose features: headings 1-3, bullet + numbered lists, blockquote, inline hyperlinks. No new migrations.

MD walker (+320 LoC):

  • New RenderMarkdownToOOXMLWithStyles entry point; RenderMarkdownToOOXML is back-compat wrapper.
  • splitMarkdownBlocks classifies lines (heading/list/quote/paragraph); 3-space indent tolerance; "N. " and "N) " numbered markers.
  • Numbered ordinal counter resets on non-list blocks ("1. A\nplain\n1. C" → 1./1.).
  • [label](url) routes through optional HyperlinkAllocator; emits <w:hyperlink r:id="…"> with "Hyperlink" char style on label runs.

Composer (+130 LoC):

  • composerLinkAllocator dedupes URLs to rIds outside the base's namespace (rIdComposer1..N).
  • patchDocumentXMLRels appends matching <Relationship Type="…/hyperlink" Target="URL" TargetMode="External"/> rows to word/_rels/document.xml.rels. Idempotent on existing rIds. Returns the patched parts slice (caller overwrites — append may grow the backing array).
  • Compose passes full stylemap to walker, not just paragraph-style.

Frontend:

  • Toolbar gains H1/H2/H3, • bullet, 1. numbered, ❞ blockquote, 🔗 link (prompts for URL).
  • domToMarkdown serializes <h1-3>, <ul>/<ol> with ordinal counter for numbered, <blockquote> line-prefix, <a href>[label](url).

Tests:

  • 8 new MD walker tests (each construct + nil-allocator fallback + detectBlockMarker matrix with 13 cases).
  • 3 new composer end-to-end tests (headings+lists, hyperlink rels wiring, URL dedupe).

Build hygiene: go build/vet/test + bun run build all clean.

Trade-offs documented:

  • List prefixes are visible "• " / "N. " text runs, not Word <w:numPr> markup. Honours stylemap entry without needing numbering.xml mutations; lawyer can apply real Word list style post-export.
  • Editor initial paint still textContent = md so toolbar-applied formatting reverts to literal MD after autosave. Acceptable Slice D trade; MD→DOM paint is a future polish.

Hard rules honoured:

  • NO new migrations.
  • NO behavior change for pre-Composer drafts.
  • {{rule.X}} aliases preserved (walker passes placeholders verbatim).
  • Q2/Q9 ratifications preserved.

Status: SLICE D COMPLETE — ready for merge. Head dispatches Slice E (specialist bases) next.

## Slice D shipped on `mai/cronus/coder-composer-slice-d` @ 6778497 Rich-prose features: headings 1-3, bullet + numbered lists, blockquote, inline hyperlinks. No new migrations. **MD walker (+320 LoC):** - New `RenderMarkdownToOOXMLWithStyles` entry point; `RenderMarkdownToOOXML` is back-compat wrapper. - `splitMarkdownBlocks` classifies lines (heading/list/quote/paragraph); 3-space indent tolerance; "N. " and "N) " numbered markers. - Numbered ordinal counter resets on non-list blocks ("1. A\nplain\n1. C" → 1./1.). - `[label](url)` routes through optional `HyperlinkAllocator`; emits `<w:hyperlink r:id="…">` with "Hyperlink" char style on label runs. **Composer (+130 LoC):** - `composerLinkAllocator` dedupes URLs to rIds outside the base's namespace (rIdComposer1..N). - `patchDocumentXMLRels` appends matching `<Relationship Type="…/hyperlink" Target="URL" TargetMode="External"/>` rows to `word/_rels/document.xml.rels`. Idempotent on existing rIds. Returns the patched parts slice (caller overwrites — append may grow the backing array). - Compose passes full stylemap to walker, not just paragraph-style. **Frontend:** - Toolbar gains H1/H2/H3, • bullet, 1. numbered, ❞ blockquote, 🔗 link (prompts for URL). - `domToMarkdown` serializes `<h1-3>`, `<ul>`/`<ol>` with ordinal counter for numbered, `<blockquote>` line-prefix, `<a href>` → `[label](url)`. **Tests:** - 8 new MD walker tests (each construct + nil-allocator fallback + detectBlockMarker matrix with 13 cases). - 3 new composer end-to-end tests (headings+lists, hyperlink rels wiring, URL dedupe). **Build hygiene:** go build/vet/test + bun run build all clean. **Trade-offs documented:** - List prefixes are visible "• " / "N. " text runs, not Word `<w:numPr>` markup. Honours stylemap entry without needing numbering.xml mutations; lawyer can apply real Word list style post-export. - Editor initial paint still `textContent = md` so toolbar-applied formatting reverts to literal MD after autosave. Acceptable Slice D trade; MD→DOM paint is a future polish. **Hard rules honoured:** - NO new migrations. - NO behavior change for pre-Composer drafts. - `{{rule.X}}` aliases preserved (walker passes placeholders verbatim). - Q2/Q9 ratifications preserved. **Status:** SLICE D COMPLETE — ready for merge. Head dispatches Slice E (specialist bases) next.
Author
Collaborator

Slice E shipped on mai/cronus/coder-composer-slice-e @ 94310ba

Specialist base templates + base-swap content survival + generic generator.

Mig 150 seeds two new firm-agnostic submission_bases rows:

  • lg-duesseldorf (proceeding_family de.inf.lg) — Times New Roman 11pt, plain headings
  • upc-formal (proceeding_family upc.inf.cfi) — Calibri 11pt, UPC-blue (#1F3864) headings, Cambria italic blockquote

Both share the same 10-section spec.defaults shape; stylemaps target LG-Body / UPC-Body and family-prefixed Heading1-3 / ListBullet / ListNumber / Quote styles.

scripts/gen-submission-base/main.go (NEW, ~240 LoC) — generic generator with -preset flag (lg-duesseldorf | upc-formal). Each preset hard-codes typography; output is byte-reproducible. Emits minimal Composer-mode .docx (Content_Types + rels + styles.xml + anchor-only document.xml + empty rels envelope for Slice D hyperlink rels patch to splice into).

Gitea uploads (via mAi):

  • 6 - material/Templates/Word/Paliad/Composer/lg-duesseldorf.docx → 82f57b3c
  • 6 - material/Templates/Word/Paliad/Composer/upc-formal.docx → 41b9a388

Backend wiring: two new fileRegistry slugs + composerBaseSlugMap rows so fetchComposerBaseBytes routes the new catalog rows to the new Gitea objects.

Test: TestComposer_BaseSwapPreservesContent pins Q10's contract — same draft composed against HLC vs LG stylemap → content survives both ways, stylemap diff visible in OOXML, no cross-leakage.

Build hygiene: go build/vet/test + bun run build all clean.

Hard rules honoured:

  • Migration purely additive (ON CONFLICT DO NOTHING).
  • NO behavior change for pre-Composer drafts.
  • NO behavior change for existing hlc-letterhead / neutral.
  • {{rule.X}} preserved.
  • Q10 base-swap-content-survival pinned by new test.

Status: SLICE E COMPLETE — ready for merge. Head dispatches Slice F (reorder / hide / add-custom-section) next + final.

## Slice E shipped on `mai/cronus/coder-composer-slice-e` @ 94310ba Specialist base templates + base-swap content survival + generic generator. **Mig 150** seeds two new firm-agnostic `submission_bases` rows: - `lg-duesseldorf` (proceeding_family `de.inf.lg`) — Times New Roman 11pt, plain headings - `upc-formal` (proceeding_family `upc.inf.cfi`) — Calibri 11pt, UPC-blue (#1F3864) headings, Cambria italic blockquote Both share the same 10-section spec.defaults shape; stylemaps target LG-Body / UPC-Body and family-prefixed Heading1-3 / ListBullet / ListNumber / Quote styles. **`scripts/gen-submission-base/main.go` (NEW, ~240 LoC)** — generic generator with `-preset` flag (lg-duesseldorf | upc-formal). Each preset hard-codes typography; output is byte-reproducible. Emits minimal Composer-mode .docx (Content_Types + rels + styles.xml + anchor-only document.xml + empty rels envelope for Slice D hyperlink rels patch to splice into). **Gitea uploads (via mAi):** - `6 - material/Templates/Word/Paliad/Composer/lg-duesseldorf.docx` → 82f57b3c - `6 - material/Templates/Word/Paliad/Composer/upc-formal.docx` → 41b9a388 **Backend wiring:** two new `fileRegistry` slugs + `composerBaseSlugMap` rows so `fetchComposerBaseBytes` routes the new catalog rows to the new Gitea objects. **Test:** `TestComposer_BaseSwapPreservesContent` pins Q10's contract — same draft composed against HLC vs LG stylemap → content survives both ways, stylemap diff visible in OOXML, no cross-leakage. **Build hygiene:** go build/vet/test + bun run build all clean. **Hard rules honoured:** - Migration purely additive (ON CONFLICT DO NOTHING). - NO behavior change for pre-Composer drafts. - NO behavior change for existing hlc-letterhead / neutral. - {{rule.X}} preserved. - Q10 base-swap-content-survival pinned by new test. **Status:** SLICE E COMPLETE — ready for merge. Head dispatches Slice F (reorder / hide / add-custom-section) next + final.
Author
Collaborator

Slice F — FINAL — shipped on mai/cronus/coder-composer-slice-f @ bd7896e

Reorder / delete / add custom section. Closes Composer A→F.

Backend:

  • SectionService.Create (slug + labels + kind validation; auto-assigns order_index when 0; UNIQUE collision → ErrInvalidInput)
  • SectionService.Delete
  • SectionService.Reorder (transactional; rewrites order_index to (1..N)×10)
  • 3 new handlers: POST /sections, DELETE /sections/{id}, POST /sections/reorder

Frontend:

  • Drag-and-drop reorder via HTML5 native DnD on a ⋮⋮ per-section handle (scoped so contentEditable selections inside the editor still work). Drop-target highlight + cleanup on dragend/drop/cancel.
  • Per-section Delete button with confirm prompt.
  • "+ Add section" inline form (slug + DE/EN labels + kind dropdown) below the section list.
  • ~65 LoC CSS for grab cursor + drop-target accent + add-section form layout.

Tests: TEST_DATABASE_URL-gated live integration test exercising Create (with collision check) + Delete (with second-delete-returns-not-found) + Reorder (reverses 10 seeded sections + checks ascending order_index).

Build hygiene: go build/vet/test + bun run build all clean.

Hard rules honoured:

  • NO new migrations.
  • NO behavior change for pre-Composer drafts.
  • Q2/Q9/Q10 ratifications preserved.
  • {{rule.X}} aliases preserved.

Composer A→F complete

The full feature set ratified by m on 2026-05-26 is in place:

  • A — base picker + section list shell (migs 146/147/148)
  • B — editable prose + anchor-spliced render + MD→OOXML walker
  • C — building blocks library + section picker (mig 149)
  • D — rich prose (headings, lists, blockquote, hyperlinks)
  • E — specialist bases lg-duesseldorf + upc-formal (mig 150)
  • F — section reorder / delete / add custom

All 5 migrations cumulative-apply at Dokploy boot. All four .docx files (_skeleton.docx, _firm-skeleton.docx, lg-duesseldorf.docx, upc-formal.docx) are in Gitea Composer-mode (anchor-only bodies). 4 ratification-deviations from the original design held throughout: Q2 plain-text BB paste (no lineage), Q6 auto-upgrade rule (NOT in this slice train — explicitly deferred by head), Q10 fully-editable seeded sections, Q12 dissolved by Q2.

Status: SLICE F COMPLETE — Composer A→F ready for merge.

## Slice F — FINAL — shipped on `mai/cronus/coder-composer-slice-f` @ bd7896e Reorder / delete / add custom section. Closes Composer A→F. **Backend:** - `SectionService.Create` (slug + labels + kind validation; auto-assigns order_index when 0; UNIQUE collision → ErrInvalidInput) - `SectionService.Delete` - `SectionService.Reorder` (transactional; rewrites order_index to (1..N)×10) - 3 new handlers: POST /sections, DELETE /sections/{id}, POST /sections/reorder **Frontend:** - Drag-and-drop reorder via HTML5 native DnD on a `⋮⋮` per-section handle (scoped so contentEditable selections inside the editor still work). Drop-target highlight + cleanup on dragend/drop/cancel. - Per-section Delete button with confirm prompt. - "+ Add section" inline form (slug + DE/EN labels + kind dropdown) below the section list. - ~65 LoC CSS for grab cursor + drop-target accent + add-section form layout. **Tests:** TEST_DATABASE_URL-gated live integration test exercising Create (with collision check) + Delete (with second-delete-returns-not-found) + Reorder (reverses 10 seeded sections + checks ascending order_index). **Build hygiene:** go build/vet/test + bun run build all clean. **Hard rules honoured:** - NO new migrations. - NO behavior change for pre-Composer drafts. - Q2/Q9/Q10 ratifications preserved. - `{{rule.X}}` aliases preserved. ## Composer A→F complete The full feature set ratified by m on 2026-05-26 is in place: - **A** — base picker + section list shell (migs 146/147/148) - **B** — editable prose + anchor-spliced render + MD→OOXML walker - **C** — building blocks library + section picker (mig 149) - **D** — rich prose (headings, lists, blockquote, hyperlinks) - **E** — specialist bases `lg-duesseldorf` + `upc-formal` (mig 150) - **F** — section reorder / delete / add custom All 5 migrations cumulative-apply at Dokploy boot. All four `.docx` files (`_skeleton.docx`, `_firm-skeleton.docx`, `lg-duesseldorf.docx`, `upc-formal.docx`) are in Gitea Composer-mode (anchor-only bodies). 4 ratification-deviations from the original design held throughout: Q2 plain-text BB paste (no lineage), Q6 auto-upgrade rule (NOT in this slice train — explicitly deferred by head), Q10 fully-editable seeded sections, Q12 dissolved by Q2. **Status:** SLICE F COMPLETE — Composer A→F ready for merge.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/paliad#141
No description provided.