Commit Graph

1385 Commits

Author SHA1 Message Date
mAi
b582e62b2b Merge: t-paliad-364 styled+filled submissions — project-less caption fill (P3b) + merge-safe styled firm-skeleton generator (P3a Option B)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 16:17:19 +02:00
mAi
f152109bd3 Merge remote-tracking branch 'origin/main' into mai/bohr/coder-generation-core 2026-06-01 16:17:08 +02:00
mAi
c1781c9a89 feat(generation): t-paliad-364 styled+filled submissions — project-less caption fill (P3b) + merge-safe styled firm-skeleton generator (P3a Option B)
P3(b) fill-what-we-can (submission_vars.go):
The project-less branch of SubmissionVarsService.Build ran only firm/today/
user/proceduralEvent resolvers, so caption.*/project.proceeding.* never
populated and every Rubrum value rendered [KEIN WERT]. The rule is already
loaded on this path and carries ProceedingTypeID. Now the branch loads the
proceeding type via loadProceedingType(rule.ProceedingTypeID) (tolerates a nil
id) and appends a proceeding-only projectResolver{project:nil} + a
captionResolver{project:nil} — both nil-project safe. Result: caption heading /
designations / versus / subject + the proceeding line fill from the
submission_code's proceeding; only party names / case number / court stay blank
for the lawyer. Preserves the "Ohne Projekt" affordance (t-paliad-243).

addProjectVars is now nil-project safe (guards the project.* direct fields,
keeps the pt-driven project.proceeding.* block) so projectResolver can serve as
the proceeding-only resolver. Pinned by TestProjectlessFill_* +
TestAddProjectVars_NilProjectFillsProceedingOnly (no DB).

P3(a) styling — merge-safe styled firm-skeleton (scripts/gen-hl-skeleton-template):
Generation landed on docx.BuildFallbackSkeleton (generic Heading1/2/Normal)
because the firm-skeleton's body had been repurposed into an anchors-only
Composer base, which HasMergePlaceholders rejects. Rewrote the generator to take
the deployed clean .docx carrier and replace ONLY word/document.xml with a clean
caption-driven Rubrum that uses the firm Rubrum styles (Table-Recitals-Party/
PartyDetails/PartyRoles/Sequencers, Heading-H2, Signature, Body-B0) and the same
{{key}}/{{caption.*}} placeholders the in-process fallback uses — preserving the
carrier's styles/theme/numbering/letterhead/logo and its sectPr verbatim. Adds a
-lang flag (DE _firm-skeleton.docx, EN _skeleton.en.docx) and auto-detects the
firm style prefix (HLpat-/HLCpat-) so it stays correct across the .dotm rebrand
drift. The resolver's tier-4/tier-3 merge-safe guard auto-prefers the restored
templates — no handler change.

Dry-run (TEST_DATABASE_URL, de.inf.lg.erwidg): project-less render fills caption
heading="In dem Rechtsstreit"/Klägerin/gegen/Beklagte/Patentverletzung + the
proceeding line, only party/case/court blank; full-project render additionally
fills parties + case number + court. Both carry the HLpat-Table-Recitals-* /
HLpat-Heading-H2 / HLpat-Signature styles, HasMergePlaceholders=true, no
{{#section}} junk.
2026-06-01 16:14:28 +02:00
mAi
72ba8fb2b9 Merge: t-paliad-365 picker quality — re-kind 8 court-act rules (mig 164) + group-header contrast
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 15:59:19 +02:00
mAi
13166cd784 fix(submissions): t-paliad-365 picker quality — re-kind 8 court-act rules (mig 164) + group-header contrast
P1: 8 anchor/trigger rows (Zustellung des Urteils / Veröffentlichung der
Erteilung) were mislabeled event_type='filing'+primary_party='both' and leaked
into the /submissions/new draftable-submission picker. Migration 164 re-kinds
them to event_type='decision'+primary_party='court', aligning them with the 16
sibling court-act rows the model already has; the picker's event_type='filing'
filter then excludes them. Defensive guard added to loadSubmissionCatalog
(primary_party IS DISTINCT FROM 'court') as belt-and-braces against future drift.
Safety: the only event_type coupling for these rows is ruleAnchorKind
(projection_service.go) — they now anchor as appointments (correct, sibling-
consistent); deadline computation keys off is_court_set not event_type, and the
child sequence guard parentHasAnchoredActual UNIONs both anchor tables, so no
chain breaks. Verified: catalog 113->105, 8 court acts gone, 48 'both' party
submissions (incl. UPC appeal briefs) retained.

P2: the grouped picker table already renders a correct colspan group-header row
(a911a2d); the defect was contrast — the band used --color-bg-subtle, the SAME
token as the thead, so groups read as undelimited floating text. New
--color-bg-group-header token (cool/deeper in light, raised-cream in dark) +
heavier top divider + neutral left accent + darker label make each proceeding a
distinct section. Live deployed bundle confirmed current (not stale).
2026-06-01 15:58:32 +02:00
mAi
9e515551b2 Merge: t-paliad-363 generation-quality diagnosis (picker filter, layout, Rubrum styling+fill)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 15:50:56 +02:00
mAi
63e973df88 docs(generation-quality): t-paliad-363 diagnosis — picker court-act mislabel, group-header contrast, unstyled+unfilled Rubrum (2-layer P3 + 3 forks) 2026-06-01 15:48:11 +02:00
mAi
8eba20f46a Merge: t-paliad-361 caption-wording — Respondent + restored Streitpatent line + DE appeal/nullity role-label backfill (mig 163, lexy-confirmed)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 15:23:09 +02:00
mAi
d30556397a Merge remote-tracking branch 'origin/main' into mai/bohr/coder-apply-m-s-caption 2026-06-01 15:22:56 +02:00
mAi
1882468780 feat(caption): apply m's caption-wording decisions — Respondent + Streitpatent line + DE appeal/nullity role labels
Some checks are pending
Paliad CI gate / build (push) Waiting to run
Paliad CI gate / test-go (push) Waiting to run
Paliad CI gate / deploy (push) Blocked by required conditions
t-paliad-361, follow-up to t-paliad-358 A-S2. m ruled on the 7 lexy-wording
flags (AskUserQuestion 2026-06-01 14:30). Most flags CONFIRMED the live
wording; three changes land here, all caption (Rubrum) wording, all in one
reversible migration 163.

Change 1 — UPC appeal responding party (EN): 'Appellee' → 'Respondent'.
  Fixed at the data source: the mig-137 role-label override on
  upc.apl.unified (the only place 'Appellee' was stored). The caption
  resolver's instance-derived EN fallback already said 'Respondent', so no
  code change. DE side (Berufungsbeklagter) untouched per m.

Change 2 — restore the standalone 'Streitpatent: {{project.patent_number_upc}}'
  (DE) / 'Patent in suit:' (EN) line in the upc-formal Composer caption seed,
  dropped in A-S2 (mig 161). Keeps the parametric 'In der Sache' heading (m did
  not revert that). Only the upc-formal base is touched; grouped with the case
  number ahead of {{project.court}}.

Change 3 — backfill lexy-confirmed role-label overrides for the four DE
  appeal/nullity proceedings that carried none, so designations are correct
  even when project.instance_level is unset (statute-grounded: §§ 511/542/544
  ZPO, §§ 81/110 PatG; bracketed-inclusive gender style):
    de.inf.olg    Berufungskläger(in) / Berufungsbeklagte(r)              // Appellant / Respondent
    de.inf.bgh    Revisionskläger(in) / Revisionsbeklagte(r)              // Appellant / Respondent
    de.null.bpatg Nichtigkeitskläger(in) / Beklagte(r) (Patentinhaber(in)) // Nullity claimant / Defendant (patent proprietor)
    de.null.bgh   Berufungskläger(in) / Berufungsbeklagte(r)              // Appellant / Respondent

Updates submission_vars_caption_test.go: adds EN assertions + cases pinning the
Respondent change and the four backfilled designations (each with instance_level
unset, proving the override path). go vet + go test ./... + bun build clean.
2026-06-01 15:21:37 +02:00
mAi
3042bea3c2 Merge: patentstyle Content-Disposition derives filename from requested file (HLC install fix)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 15:18:32 +02:00
mAi
ea9dd261ae fix(patentstyle): derive Content-Disposition filename from requested file (HLC download saved under wrong name, install macro rejected) 2026-06-01 15:18:32 +02:00
mAi
78f9aa4f46 Merge: patentstyle download href flip → HLC-Patents-Style.dotm
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 15:04:43 +02:00
mAi
19f9604827 fix(patentstyle): flip download href to HLC-Patents-Style.dotm (m blocked on HL file) 2026-06-01 15:04:43 +02:00
mAi
c303c01652 chore(patentstyle): publish HLC Patents Style v0.260601
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
sha256 DE6B6A17AC603FF4A9B3893CD2A7EF8263C9E2D4224A0A5E28E2FABF5E27A798
Source: HL/mWorkRepo#37 (Build.ps1 -> publish.sh pipeline).
2026-06-01 14:52:35 +02:00
mAi
97a2742f10 Merge: patentstyle landing page — English + HLC rebrand (work/paliad coordination)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 13:28:04 +02:00
mAi
b26360111a docs(patentstyle): English + HLC rebrand of landing page (work/paliad coordination) 2026-06-01 13:27:57 +02:00
mAi
e914bac79a chore(patentstyle): publish HL Patents Style v0.260601
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
sha256 D4D0BDD31CC2C4F9A2362363FEC2A7D86B8BE8A4EA7B0F2CEF5F1944A15B3A4A
Source: HL/mWorkRepo#37 (Build.ps1 -> publish.sh pipeline).
2026-06-01 13:17:15 +02:00
mAi
713a4d4206 Merge: t-paliad-358 A-S3 — firm-agnostic merge-fallback + firm-skeleton letterhead (firm.name placeholderised) — COMPLETES Rubrum Option A
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 13:11:16 +02:00
mAi
cd3cd0230c Merge remote-tracking branch 'origin/main' into mai/bohr/coder-rubrum-letterhead 2026-06-01 13:11:05 +02:00
mAi
cd793b1d98 feat(submissions): firm-agnostic merge-fallback letterhead (t-paliad-358 A-S3)
A-S3 part 1 (merge-fallback letterhead, in-repo): the fallback skeleton
(docx.BuildFallbackSkeleton) gains a minimal Word page-header letterhead
(word/header1.xml) carrying only {{firm.name}}, filled from branding by the
variable bag. A generated fallback-path document now repeats a correct,
firm-agnostic firm identity on every page (the firm name moved out of the body
into the header; no hard-coded firm name anywhere). Wired the header part:
Content_Types override + document.xml.rels relationship (rId2) + sectPr
headerReference; document gains xmlns:r. Deliberately minimal — the fallback is
a starter, not full firm chrome.

A-S3 part 2 (firm-skeleton .docx, mWorkRepo — separate commit): _firm-skeleton.docx
footer1's "Firm name" SDT content control hard-coded "Hogan Lovells" → now
{{firm.name}}, filled by the Composer's final renderer pass / merge.go (both run
SubmissionRenderer over header/footer parts). Surgical <w:t> edit + repack (all
38 parts preserved, verified renders + fills cleanly, integrity OK). Pushed to
HL/mWorkRepo as mAi (commit 5a3a1722).

Path taken: option (a) for the firm NAME (cleanly placeholderised). NOT option
(a) for footer2's HL legal-entity boilerplate (registered no. OC323639, LLP
structure, 40+ office cities) — token-swapping the name into it would assert
false legal facts for a non-HLC firm; left intact and flagged (needs per-firm
legal-footer config, not templating). No corrupt .docx shipped.

Completes the Rubrum + letterhead auto-fill Option A train (A-S1 signature_block
+ generate-fallback fix; A-S2 parametric caption.*; A-S3 firm-agnostic letterhead).

Tests: fallback header asserts {{firm.name}} present + renders firm-agnostically
(de+en); patched firm-skeleton verified to render + fill (transient check).
go vet ./... + bun build clean; touched packages green.
2026-06-01 13:10:04 +02:00
mAi
a50ddc3927 Merge: t-paliad-356 Slice 5 — firm-wide default name compositions (mig 162) — COMPLETES nomen train
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 13:05:50 +02:00
mAi
c639c5695c Merge remote-tracking branch 'origin/main' into mai/fermi/coder-implement-nomen 2026-06-01 13:05:30 +02:00
mAi
a05ae1f2ae feat(settings): firm-wide default name compositions (t-paliad-356 Slice 5)
Completes the nomen train (S1–S5). Adds the FIRM tier of the name-composition
precedence chain — per-document → user → FIRM → system (PRD §3.1/§3.2) —
mirroring firm_dashboard_default exactly.

Storage + service:
- Migration 162: paliad.firm_name_compositions singleton (id=1, CHECK id=1,
  RLS read-all + service-role writes) — same shape as firm_dashboard_default
  (mig 117), holding a validated { artifact_id: Composition } jsonb map.
- FirmNameCompositionService (Get/Set/Clear) + getFirmNameCompositions /
  setFirmNameCompositions / clearFirmNameCompositions singleton helpers in
  name_composition_spec.go.

Resolution:
- resolveComposition is now variadic over ordered specs (user, firm); first
  valid wins, else system default. Existing single-spec callers unchanged.
- Render path threads the firm tier: renderSubmissionDraftTitle /
  RenderSubmissionFilenameFor gain a firm param; newDraftName +
  submissionDownloadFilename load it (nil-safe). A firm default thus changes
  the effective name for every user without a personal override.

Admin surface (mirrors firm_dashboard_default):
- GET/PUT/DELETE /api/admin/name-compositions{/artifact_id} (adminGate) read
  back / set / clear the firm default per artifact.
- /settings Namensschemata cards gain an admin-only "Firmenstandard" block
  (set from the current template field / clear) revealed via is_admin, plus a
  "Firmenstandard" badge for non-admin users whose effective name comes from
  the firm tier. SettingsNameArtifact now resolves user→firm→system and
  exposes firm_is_set/firm_template.

Tests: pure precedence (user>firm>system) + firm-tier view + live firm
round-trip/Validate-rejection (via db.ApplyMigrations). go vet, go test ./...,
bun build all clean; gated live tests green against TEST_DATABASE_URL.

NOTE (merge ordering): golang-migrate is forward-only. Migration 162 must not
reach a DB before bohr's 161 (Rubrum Composer seed) exists, or 161 will be
skipped (current>161 → never applied). Merge 161 before/with 162.

Browser Playwright of the admin firm controls deferred to post-deploy
mai-tester — shared Supabase login wall blocks pre-merge browser login (same
ceiling as t-paliad-354).
2026-06-01 13:04:11 +02:00
mAi
7fe37bb550 Merge: t-paliad-358 A-S2 — parametric caption.* keys unify Rubrum across all render paths (mig 161)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 13:01:21 +02:00
mAi
57310ab3a4 Merge remote-tracking branch 'origin/main' into mai/bohr/coder-rubrum-letterhead 2026-06-01 13:00:54 +02:00
mAi
b99b6d6fb5 feat(submissions): parametric caption.* keys — unify Rubrum across all render paths (t-paliad-358 A-S2)
Promotes the case caption (Rubrum) to ONE parametric set of resolver keys
(caption.*) consumed identically by every render path, so the wording no
longer diverges per path and reflects the forum.

New caption.* keys (addCaptionVars, submission_vars.go), each in bare +
_de + _en forms (bare resolves to the draft language):
  caption.heading · caption.claimant_designation · caption.defendant_designation
  caption.versus · caption.subject

Parametrised from data already in the bag — NO new schema:
  - designations reuse the proceeding-type role-label overrides (mig 137:
    upc.apl.unified→Berufungskläger, upc.rev.cfi→Antragsteller (Nichtigkeit),
    epa.opp.*→Einsprechende(r)/Patentinhaber(in)); else instance-derived
    appeal/cassation (project.instance_level); else civil default Klägerin/
    Beklagte // Claimant/Defendant.
  - heading + subject from jurisdiction + the dotted code's nature segment
    (inf→"In dem Rechtsstreit"/"Patentverletzung", null→"In der
    Patentnichtigkeitssache", UPC→"In der Sache"/"In the matter",
    opp→"Im Einspruchsverfahren").
Also exposes project.proceeding.jurisdiction.

All three render paths now reference the SAME keys:
  1. docx.BuildFallbackSkeleton (merge fallback) — heading/designations/versus/
     wegen-subject are {{caption.*}} placeholders.
  2. demo per-code template de.inf.lg.erwidg.docx (mWorkRepo) — regenerated via
     scripts/gen-demo-submission-template; caption wired to caption.*; closing
     drops the duplicate {{firm.name}} line (now carried by signature_block).
     Pushed to HL/mWorkRepo as mAi (commit 3682299).
  3. Composer caption seeds — mig 161 rewrites the caption section seed_md of
     all 4 bases (hlc-letterhead, neutral, lg-duesseldorf, upc-formal) to the
     parametric form (position-independent jsonb_agg patch; reversible down).

our_side is intentionally NOT a caption driver — the caption designates both
parties by procedural role regardless of which side we act for.

Tests: resolveCaption forum matrix (DE-LG/BPatG/UPC/role-label/instance-appeal/
EPA-opp), bare-resolves-to-lang, fallback skeleton renders caption.* keys.
mig 161 passes TestMigrations_DryRun. go vet ./... + bun build clean; all
touched packages green (the live approval/migration_136 failures are
pre-existing shared-DB env issues, unrelated).

LEXY-REVIEW FLAGS in the report — DE caption conventions are practitioner
convention, not in the youpc corpus; specific wordings flagged for sign-off.
2026-06-01 12:59:29 +02:00
mAi
5468a7259d Merge: t-paliad-356 Slice 4 — name-composition token-template editor on /settings
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 12:47:21 +02:00
mAi
230306518d Merge remote-tracking branch 'origin/main' into mai/fermi/coder-implement-nomen 2026-06-01 12:46:58 +02:00
mAi
6e56b9d51f feat(settings): name-composition token-template editor (t-paliad-356 Slice 4)
Adds the /settings "Namensschemata" tab so users can customise the two wired
name artifacts (submission_draft_title, submission_docx_filename) via a
single-line {token} template, with a clickable palette, live preview, and
reset-to-default — PRD §7.

Engine (pure, pkg/nomen):
- Composition.Template() serialises a composition to "{var}" shorthand;
  ParseTemplate() is its inverse — tokens + literal separators (trailing,
  owned by the left segment) + paren Wrap. Missing-rules are NOT in the
  shorthand (PRD §7); the parser leaves every segment KindOmit. Leading /
  trailing literals are rejected (the trailing-separator model can't carry
  them) so a save never silently drops characters. Table + round-trip tests.

Paliad glue (internal/services/name_template.go):
- ParseNameTemplate overlays each segment's missing-rule from the artifact's
  system default and validates against the catalog.
- PreviewNameComposition renders against the fixed PRD sample (Bayer AG / UPC
  / Sandoz / UPC_CFI_123/2026 / today) and an empties resolver so the
  missing-rule behaviour is visible. The frontend never parses templates —
  the nomen engine stays the single source of truth.
- SettingsNameArtifacts / SettingsNameArtifact build the per-artifact cards
  (current template, system default, override flag, ordered palette, previews).

API (internal/handlers/name_compositions.go):
- GET    /api/me/name-compositions               — cards
- POST   /api/me/name-compositions/preview        — live preview + validation
- PUT    /api/me/name-compositions/{artifact_id}   — store override
- DELETE /api/me/name-compositions/{artifact_id}   — reset to system default
Storage reuses the Slice-3 service surface (UserNameCompositions /
SetUserNameCompositions) via read-modify-write; no new column, no migration.

Frontend: new tab + JS-built cards (palette insert-at-cursor, 250ms-debounced
preview, save/reset, DE/EN labels), CSS, and i18n keys (de + en).

Gates: go vet, go test ./..., bun build all clean. Browser verification of the
settings UX is deferred to post-deploy mai-tester — the shared Supabase login
wall blocks pre-merge browser login (same ceiling as t-paliad-354).
2026-06-01 12:46:07 +02:00
mAi
375d631f1b Merge: t-paliad-358 A-S1 — fill firm.signature_block + fix generate-fallback junk (merge-safe Rubrum fallback)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 12:40:53 +02:00
mAi
e3a604b4c4 Merge remote-tracking branch 'origin/main' into mai/bohr/coder-rubrum-letterhead 2026-06-01 12:40:40 +02:00
mAi
0763b7daa2 feat(submissions): fill firm.signature_block + fix generate-fallback junk (t-paliad-358 A-S1)
Two letterhead/Rubrum auto-fill fixes (Option A, no schema change):

1. firm.signature_block: was hardcoded "" ("reserved for Phase 2"), so every
   template referencing {{firm.signature_block}} rendered blank. Now filled
   from branding.Name — the firm identity line of a submission's signature
   block (the signature section seeds with signature_block + user.display_name).
   Firm-agnostic: a FIRM_NAME redeploy signs with the right firm.

2. Generate-fallback junk (kepler audit §1 Path 3): resolveSubmissionTemplate is
   the merge-path resolver (every caller feeds merge.go), but its lower tiers
   fetched _firm-skeleton.docx / _skeleton.docx — which were repurposed into
   anchors-only Composer bases (t-paliad-313 Slice B). Their bodies hold only
   {{#section:KEY}} markers, which placeholderRegex ignores, so merge.go emitted
   them verbatim as literal "{{#section:letterhead}}…" junk for every code
   without a per-code template (i.e. everything except de.inf.lg.erwidg).

   Fix:
   - docx.BuildFallbackSkeleton(lang): in-process, lang-aware, merge-safe basic
     Schriftsatz with a data-driven basic Rubrum (real {{key}} placeholders the
     var bag fills). Always available, no Gitea round-trip.
   - docx.HasMergePlaceholders guards tiers 3/4/5: a fetched skeleton is used
     only if it carries real placeholders, else we fall through to the embedded
     fallback. Today's anchors-only/placeholder-free files are skipped; a future
     merge-safe firm-skeleton (with letterhead) is preferred again automatically.
   - merge.go strips stray {{#section:…}}/{{/section:…}} markers defensively so
     no anchors-only carrier can ever leak Composer junk into a merged document.

Verified: confirmed live that deployed _firm-skeleton.docx + _skeleton.docx are
anchors-only (fetch+unzip); unit tests cover BuildFallbackSkeleton rendering a
real Rubrum (de+en), HasMergePlaceholders classification, marker stripping, and
the signature_block fill. go build / vet ./... / test ./... + bun build clean.

Out of scope (flagged for next slices): demo template's closing prints
{{firm.name}} then {{firm.signature_block}} (=firm.name) → A-S2 dedups the demo
wording. Restoring firm letterhead chrome to the merge fallback → A-S3.
2026-06-01 12:39:53 +02:00
mAi
c4e3a74e35 Merge: t-paliad-359 regroup draft-editor sidebar — naming (name+keyword) then template (base+language)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 12:38:08 +02:00
mAi
9d234f275f fix(submissions): regroup draft-editor sidebar — naming (name+keyword) then template (base+language) 2026-06-01 12:37:26 +02:00
mAi
83d5ed27e0 Merge: t-paliad-356 Slice 3 — per-user name-composition overrides (system→user precedence, mig 160)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 12:21:27 +02:00
mAi
73f379d305 feat(submissions): per-user name-composition overrides, system→user precedence (t-paliad-356 Slice 3)
PRD §3. A user can now override the system-default name composition for an
artifact; rendering prefers a valid user override over the system default.
No UI yet (Slice 4) — overrides are settable via the service API + tests.

- Migration 160: users.name_compositions jsonb NOT NULL DEFAULT '{}' — a
  { artifact_id: Composition } map.
- NameCompositionSpec (internal/services/name_composition_spec.go): Validate
  (write: known artifact + segments reference the artifact's catalog vars)
  and SanitizeForRead (read: drop unknown artifacts, drop segments with
  unknown vars, clamp version) — mirrors DashboardLayoutSpec. Plus
  nomen.Composition.SanitizeForRead and lowercase JSON tags on the nomen
  types so the stored shape is stable.
- Resolution: resolveComposition(artifact, overrides) returns the user
  override when present + valid, else the system default. The firm slot
  (PRD §3.1) is reserved for Slice 5 — system is the fallback below user
  here. Wired into BOTH artifacts: the draft-title via the create path
  (newDraftName → autoNameFor{Project,NonProject}) and the .docx filename
  via RenderSubmissionFilenameFor in the three download handlers.
  AutoSubmissionTitle / RenderSubmissionFilename stay as the nil-override
  (system-default) references the #155/354 matrices pin.
- Per-document keyword override generalised: writes now land at
  composer_meta.name_overrides.keyword (the general {var:value} shape);
  reads honour both that and the legacy composer_meta.filename_keyword
  (back-compat). The read moved to services.SubmissionFilenameKeyword
  (handlers delegate) so it is live-testable.

BACK-COMPAT FINDING: the one shipped filename_keyword row needs no data
migration — the read resolves legacy composer_meta.filename_keyword as
name_overrides.keyword. Verified by a live round-trip (seed legacy jsonb →
Get → SubmissionFilenameKeyword == the legacy value).

Verified (TEST_DATABASE_URL): (a) name_compositions Set/Get round-trip +
write-time Validate rejection of an out-of-catalog variable; (b) a user
override beats the system default for the title (through Create:
'Klageerwiderung <date>' vs default '<date> Klageerwiderung') and the
filename (through RenderSubmissionFilenameFor), with the nil-override
default unchanged; (c) legacy filename_keyword back-compat read. Plus unit
tests for nomen.SanitizeForRead, NameCompositionSpec Validate/Sanitize/
resolve, and the keyword back-compat read. go vet + go test ./... (15
pkgs) + bun build clean. No Playwright (no UI this slice).
2026-06-01 12:20:19 +02:00
mAi
9a5ee93f2e Merge: t-paliad-357 Rubrum+letterhead auto-fill audit (gap map)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 12:13:51 +02:00
mAi
213be10ada docs(submissions): gap map — Rubrum + letterhead auto-fill from project data (t-paliad-357)
Read-only audit. Maps the 3 doc-generation fill paths (merge.go fills
header/footer placeholders; compose.go passes headers byte-for-byte;
skeleton-as-merge-fallback latent bug), three gap tables (template /
var-bag / data-model), forum-dependence grounded on UPC RoP r.13, and a
tracer-bullet-first wiring proposal. States the A/B fork for m: wire what
current data supports (basic caption, no schema) vs. forum-correct Rubrum
(structured party address/Rechtsform/Sitz + court details + capture UI).
2026-06-01 12:12:10 +02:00
mAi
6dd9befba3 Merge: t-paliad-356 Slice 2 — non-project drafts get date-first name via nomen engine
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 12:06:43 +02:00
mAi
e10b5e6546 feat(submissions): non-project drafts get a date-first name (t-paliad-356 Slice 2)
PRD §6. Project-less drafts no longer fall back to the bare 'Entwurf N'
counter — they render the submission_draft_title artifact through the
nomen engine, leading with the date like project drafts do (m's ask).

newDraftName's non-project branch now resolves a keyword (document type)
from the draft's submission_code and renders '<date> <keyword>', e.g.
'2026-06-01 Klageerwiderung'. When the code has no published filing rule
the keyword degrades to the localized 'Entwurf'/'Draft' word, yielding
'<date> Entwurf'. Collisions get the usual ' (N)' suffix via
uniqueDraftName.

FLAG resolved: project-less drafts DO carry a required submission_code
(the global-create handler rejects an empty code), and submission_code ->
rule name is a function across the published filing rules (the code
encodes the proceeding, e.g. de.inf.lg.erwidg -> Klageerwiderung), so the
keyword resolves project-independently via a LIMIT 1 catalog lookup. Both
branches (rule-name / Entwurf fallback) flow through the same composition.

The keyword is a new, normally-omitted segment on the title composition;
project drafts leave it empty so they render identically to #155 — the
regression guard. The identity trio and the keyword are mutually exclusive
by construction (project => trio, no project => keyword).

Verification: PRIMARY gate is a service-level live test (TEST_DATABASE_URL)
asserting Create(projectID=nil) yields '<date> <keyword>', the ' (2)'
collision suffix, EN document type, and the '<date> Entwurf'/'Draft'
fallback — all green against the real DB. Existing project-title matrix
(TestAutoSubmissionTitle) unchanged. go vet + go test ./... + bun build
clean. Browser (Playwright) verification not meaningful pre-merge: S2 is
not yet deployed (only S1/cd3f784 is) and the naming is server-side in
Create behind the shared-auth login wall.
2026-06-01 12:05:57 +02:00
mAi
cd3f7843a7 Merge: t-paliad-356 Slice 1 — nomen name-composition engine + fold in #155/354 schemes (byte-equal refactor) + PRD
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 11:57:13 +02:00
mAi
4920328b09 feat(nomen): name-composition engine + fold in the two shipped schemes (t-paliad-356 Slice 1)
Slice 1 of the filename-generator train (PRD 2026-06-01 §8). Pure refactor
behind byte-equality — no user-visible change.

pkg/nomen: the reusable engine. A Composition (ordered Segments, each with
a trailing separator, optional wrap, and an omit/placeholder/literal
missing-rule) renders against a VarResolver and a RenderTarget. Targets
split into SanitiseValue (per-variable) + Finalise (whole-string + suffix)
so a human title and a sanitised filename are two targets of one
composition. VarCatalog + Validate guard stored compositions.

internal/services/namegen.go: paliad-side wiring — the two seed system-
default compositions that reproduce AutoSubmissionTitle (#155) and
submissionFileName (354) as DATA, their variable catalogs, the resolvers
(built from the existing submission_autoname helpers), and the artifact
registry binding artifact -> catalog -> target -> default.

Repointed call-sites: AutoSubmissionTitle and handlers.submissionFileName
are now thin wrappers rendering through the registry; the assembly logic
lives in the engine. Removed the hardcoded title/filename assembly and the
handler's Az.-folgt const (now the case_number segment's placeholder).

FLAG resolved (separators): the PRD sketched LEADING separators; that can't
reproduce #155's client-absent case (date must join forum with a space
while forum->opponent stays ' ./. '). Switched to TRAILING separators
(owned by the left segment) — the minimal faithful fix. PRD §2.1 annotated.

FLAG resolved (back-compat): the shipped composer_meta.filename_keyword
override still flows through the engine — live round-trip test green.

Acceptance: all existing #155/354 test matrices pass UNCHANGED (the
byte-equality gate); new pkg/nomen unit tests cover trailing-sep, the three
missing-rules, targets, and Validate; namegen_test validates the seeds
against their catalogs. go vet + go test ./... + bun build all clean.
2026-06-01 11:56:27 +02:00
mAi
385abc7a98 docs(prd): composable name/filename generator engine (t-paliad-355)
Engine (pkg/nomen) renders a name from (composition, var-bag, render-
target). Structured segments + string shorthand; omit/placeholder/literal
missing-rules; title vs filename targets. Both shipped schemes (#155
draft title, 354 export filename) fold in as data-driven seed defaults
with byte-equality as the acceptance gate. Non-project drafts get a
date-first <date> <keyword> name (m's immediate ask).

Settings built ON the dashboard-layout-spec precedent: system -> firm ->
user -> per-document precedence, validated jsonb spec. Project-level
deferred to v1.1 (storage path reserved on projects.metadata).

5-slice train: engine+faithful-refactor -> non-project fix -> user
override -> settings UX -> firm default. All 8 grilling questions
answered (matched recommendations), captured in the decisions section.
2026-06-01 11:42:50 +02:00
mAi
94adeeb8cb Merge: t-paliad-354 generated-doc filename <date> <keyword> (<case>) + user-replaceable keyword
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 10:42:25 +02:00
mAi
d834b36313 test(submissions): live-DB round-trip for filename_keyword composer_meta merge/clear (t-paliad-354)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-06-01 10:40:55 +02:00
mAi
4092c889c4 feat(submissions): generated-doc filename <date> <keyword> (<case>) + user-replaceable keyword
Generated documents now download as "YYYY-MM-DD keyword (case number).docx"
(date first/sortable, case number bracketed) instead of the old
"rule-case-date.docx" shape.

- submissionFileName: date-led frame; keyword = user override > lang-aware
  rule name > "submission"; case number always bracketed, placeholder
  "Az. folgt" (named const) when the project has no Aktenzeichen.
- SanitiseSubmissionFileName hardened to fold the full Windows-reserved
  set (colon star question angle pipe) on top of slash/backslash, while
  preserving spaces + parentheses so the assembled frame stays
  human-facing yet filesystem-safe.
- User-replaceable keyword stored in the draft's composer_meta jsonb
  (filename_keyword, no migration). Editor gains a "Stichwort (Dateiname)"
  input that placeholders the auto rule name and persists via the draft
  PATCH path. One-click /generate has no draft row -> keeps auto keyword.

Tests: submissionFileName (full / no-AZ / override / EN / slash case-no /
blank override / empty rule), submissionFilenameKeyword, extended
sanitiser cases.

t-paliad-354
2026-06-01 10:35:23 +02:00
mAi
db1040968f Merge: t-paliad-352 submission draft auto-naming (m/paliad#155)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-05-31 15:29:32 +02:00
mAi
f292338919 feat(submissions): auto-name new drafts <date> <client>./.<forum>./.<opponent> (m/paliad#155)
New project-bound submission drafts now default to a sortable, legal-
convention title instead of the bare "Entwurf N" counter:

    <YYYY-MM-DD> <ClientName> ./. <ForumShort> ./. <OpponentName>

- Date leads (ISO, Europe/Berlin) so drafts list chronologically; " ./. "
  is the German legal "gegen" separator.
- Client = root 'client' ancestor of the project tree.
- Forum = proceeding-type jurisdiction (UPC/EPA/DPMA); German proceedings
  resolve to the deciding court (LG/OLG/BGH/BPatG) from the code tail.
- Opponent = primary opposing party, picked by our_side posture
  (active → defendant bucket, reactive → claimant bucket).
- Any segment that resolves empty is omitted with its leading separator;
  a project-less draft keeps the legacy "Entwurf N" scheme entirely.
- Create-time only: existing drafts are never renamed, and a lawyer's
  later manual rename via Update is untouched. Same-slot collisions
  de-duplicate with a " (N)" suffix.

Customization scope (per-user / firm / template, issue #155 Q4) is v1.1 —
the template is hardcoded in submission_autoname.go for now; the override
string is documented as the single extension point on AutoSubmissionTitle.

Example output:
  full:        2026-05-31 Bayer AG ./. UPC ./. Novartis Pharma
  no opponent: 2026-05-31 Bayer AG ./. BPatG
  no forum:    2026-05-31 Bayer AG ./. Novartis Pharma
  date only:   2026-05-31

AutoSubmissionTitle + segment resolvers are pure and table-tested
(submission_autoname_test.go); the Create flow is covered end-to-end
against real Postgres in submission_draft_autoname_live_test.go (gated
on TEST_DATABASE_URL).
2026-05-31 15:28:54 +02:00
mAi
2b240e7dd0 Merge: docs PRD schema corrections (planck feedback)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-05-31 15:16:55 +02:00