Files
ersatztv/docs/decisions/archive
timothy 6cf99718ee
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 13s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 34s
PR Gates / Docs update reminder (pull_request) Successful in 38s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 42s
PR Gates / decisions lifecycle (pull_request) Successful in 1m1s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 17m3s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 17m14s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 22m32s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
feat(436): arbitrary-depth rule-builder group nesting
The rule builder's Group nesting was capped at one level (#176's Kodi
model). Generalize it to recursive nesting bounded by a single shared
constant, MAX_GROUP_DEPTH (types.ts, = 5, root group is depth 0):

- parse.ts: replace the allowNested boolean with a depth counter that
  recurses to the cap; deeper input stays out of subset (null -> raw-text
  fallback), so parse remains the exact inverse of compile. Sub-group
  detection now requires the leading '(' to be the one closed by the
  trailing ')' (quote/escape aware), so '(a)x(b)' can't be mistaken for
  one wrapped group.
- RuleBuilder.tsx: 'Add group' is offered while depth < MAX_GROUP_DEPTH
  instead of only at the root; nested group boxes get box-sizing:
  border-box so per-level padding can't overflow (no global reset).
- roundtrip.test.ts: the 500-tree generator nests to the cap and asserts
  the corpus actually reached it; explicit depth-3 cases added to
  compile/parse/validation tests and a depth-gate test to RuleBuilder.

compile.ts and validation.ts already recursed correctly and are unchanged.
No backend/OpenAPI change.

Docs: spa-conventions.md §12; decisions lifecycle — new active record
spa.rulebuilder-nesting, predecessor spa.smartcollection-rule-builder
relocated to docs/decisions/archive/spa.md as superseded.

fixes #436
2026-07-25 11:01:35 +02:00
..

Archive — superseded / retired decision records

This directory holds decision records whose status is superseded or retired. They are kept verbatim (rationale prose untouched — see docs/decisions.md header and scripts/decisions_lib.py) for history: why we changed our mind is the point, never silently rewritten.

They are out of the active startup path: scripts/decisions_lib.py active_files() / all_active_records() do not glob this directory, docs/decisions/README.md (the active catalog) never lists a record from here, and an agent doing task-router discovery should not need to read this directory to find the current rule — follow a record's superseded-by key to the active successor instead.

The lifecycle validator (scripts/decisions_validate.py) still enforces invariants here:

  • a superseded/retired record MUST live under this directory, never in an active file;
  • an active record MUST NOT live under this directory;
  • supersedes/superseded-by keys must resolve reciprocally to a record in the active set OR here;
  • a record moved here must not have its rationale prose changed in the same commit (unless the commit message carries the [decisions-edit] token, reserved for genuine rationale edits).

One file per topic cluster (e.g. ci.md, release-ci-governance.md), mirroring the active docs/decisions/*.md topic-file split. See docs/decisions/migration-map.md for the legacy heading → key → status → location mapping produced during the #521 lifecycle migration.