Skip to content

Feat/react feature complete - #16

Merged
mikewolfd merged 41 commits into
mainfrom
feat/react-feature-complete
Mar 26, 2026
Merged

Feat/react feature complete#16
mikewolfd merged 41 commits into
mainfrom
feat/react-feature-complete

Conversation

@mikewolfd

Copy link
Copy Markdown
Collaborator

No description provided.

mikewolfd and others added 30 commits March 26, 2026 00:33
Hooks layer (useField, useFieldValue, useForm) bridging FieldViewModel
signals to React via useSyncExternalStore, plus auto-renderer that
walks LayoutNode trees with overridable component maps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rmEngine

New package at layer 2 providing:

Hooks layer:
- useSignal(signal) — generic Preact signal → React bridge via useSyncExternalStore
- useField(path) — full FieldViewModel state (label, hint, value, error, options, inputProps)
- useFieldValue(path) — granular value + setValue (minimal re-renders)
- useFieldError(path) — granular error string
- useForm() — form title, validity, submit
- FormspecProvider — context wrapping FormEngine + layout plan

Renderer layer:
- FormspecForm — auto-renders a definition via LayoutNode tree
- FormspecNode — recursive node renderer with field/layout dispatch
- ComponentMap — overridable field + layout component slots

Default components:
- DefaultField — semantic HTML with theme cascade + ARIA
- DefaultLayout — semantic containers (Card→section, Grid→CSS grid, Stack→div)

20 tests passing. Tree-shakeable via formspec-react/hooks subpath export.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The imperative shadcn adapter (createReactAdapter + behavior.bind) is
no longer needed. React apps should use formspec-react hooks (useField,
FormspecForm) which compose natively with any component library.

Removes React peer/dev deps, JSX config, shadcn export path, and tsx
test support from formspec-adapters.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pplication

Standalone Vite + React + Tailwind app demonstrating formspec-react:
- 30+ fields across 6 groups (org info, contact, project, budget, docs, cert)
- Custom styled components via component map overrides
- OptionSets, conditional fields (relevant), cross-field shape validation
- Required constraints with custom messages on certification checkboxes
- Validates cleanly via python3 -m formspec.validate

Run: cd examples/react-demo && npm install && npm run dev

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
14 E2E tests covering the Community Grant Application react-demo:
- Form rendering: 6 section headings, key fields, submit button
- Field interactions: text input, select dropdown, checkbox group
- Conditional field: Prior Grant ID visibility toggled by isRenewal
- Validation: 19 required-field errors on empty submit, Year Founded
  constraint (1800-2026), budget total >= requested constraint
- Certification: custom constraint messages on unchecked checkboxes
- Valid submission: full form fill, "Valid" status, Response JSON
- Shape rule: budget consistency cross-field warning

Also adds react-demo dev server (port 5200) to playwright.config.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ooks

- useWhen(expr, prefix) — reactive FEL expression evaluation for layout-level conditionals
- useRepeatCount(path) — reactive subscription to repeat instance count
- RepeatGroup — stamps template children per instance with add/remove buttons
- WhenGuard — evaluates when FEL expression and conditionally renders nodes
- rewriteBindPaths — deep-clones LayoutNode trees with index substitution

5 new tests: when-hidden, when-visible, repeat-add-button, repeat-instances, repeat-label

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…renderer tests

- FormspecProvider: touchField(), touchedVersion signal, isTouched() for dirty tracking
- useField: returns touched state, touch() method, onBlur in inputProps auto-marks touched
- 8 new tests: useWhen (2), useRepeatCount (2), touched tracking (3), renderer visibility (1)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WebView-bridged SwiftUI form renderer with @observable state,
component map overrides, and runtime locale switching. Documents
Approach C (native Rust FFI) as future migration path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…plementation status

Adds consolidated review findings from spec-expert and scout agents:
- Critical: @preact/signals-core must be peerDependency (singleton)
- Major: useSignal subscribe churn, no reactivity tests
- Feature gap table vs webcomponent (18 items, prioritized)
- Implementation status table (16 implemented features, 30 unit + 14 E2E tests)
- Architectural notes on inputProps, submit signature, inline components

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces flat review findings with a prioritized roadmap:
- v0.2: 6 items blocking real usage (signals peer dep, subscribe stability,
  initialData, registryEntries, reactivity tests, findItemByKey bug)
- v0.3: 6 rendering completeness items
- v0.4: 4 engine integration items

Updates implementation table to reflect craftsman work (33 unit tests,
touched tracking). Adds review history section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ntries, reactivity tests

- Move @preact/signals-core to peerDependencies (prevents silent
  subscription breakage from duplicate signal runtimes)
- Stabilize useSignal subscribe closure with useCallback (eliminates
  wasteful effect dispose/recreate per render cycle)
- Add initialData prop to FormspecProvider for edit flows
- Add registryEntries prop to FormspecProvider for extension validation
- Fix findItemByKey indexOf bug on duplicate path segments
- Add conditional export types conditions for bundler moduleResolution
- Add 14 new tests: signal reactivity (6), initialData (2),
  registryEntries (1), findItemByKey (4), useFieldError (1)

47 unit tests + 14 E2E tests — all passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
13-task plan covering: Swift Package scaffolding, JSONValue/LayoutNode types,
message protocol, JS bridge dispatcher, WebViewEngine, FieldState/FormState
observables, FormspecEngine public API, component map, auto-renderer, and
default SwiftUI components. Also fixes FieldItemInfo gap in design spec.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Creates the initial directory structure, Package.swift, .gitignore,
placeholder barrel source, and HTML bridge resource for the formspec-swift
Swift Package that will render formspec-defined forms as native SwiftUI views.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… fields, group relevance, a11y

- Display node rendering: Heading→h3, Text→p, Divider→hr, Alert→div[role=status]
- SubmitButton: onSubmit prop on FormspecForm, renders button, calls
  with {response, validationReport} on click
- disabledDisplay 'protected': irrelevant fields render as disabled
  with formspec-protected class instead of being hidden
- Group-level relevance: RelevanceGatedLayout subscribes to
  engine.relevantSignals[bindPath], hides group containers when irrelevant
- A11y fix: error element conditionally rendered only when field.error
  is truthy (no empty role="alert" announcements)
- Export SubmitResult type from both barrels

59 unit tests + 14 E2E tests — all passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements JSONValue with cases for all JSON types: string, number,
bool, null, array, object. Decodes in nil→Bool→Double→String→Array→Object
order so true/false is never misread as 1.0/0.0. Includes convenience
accessors and full Equatable/Hashable/Sendable conformances.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the full set of structural types for the form rendering tree:
LayoutNode (with FieldItemInfo, Presentation, AccessibilityInfo),
NodeCategory, LabelPosition, ResolvedOption, DisabledDisplay,
ValidationMode, ValidationReport, ResolvedValidationResult,
ValidationSeverity, ValidationSummary, and FormspecError. All types
are Codable/Sendable. Includes a simple-layout.json fixture and 16 tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RenderingBundle carries everything the engine needs to initialize a form:
required definition + layoutPlan, plus optional component, theme, registry,
locales, defaultLocale, and runtimeContext. RuntimeContext injects ambient
metadata (meta map, IANA timeZone, deterministic seed). Both are Codable/
Sendable with memberwise inits defaulting all optionals to nil. 6 tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nal validation, submit metadata

- Add runtimeContext prop to FormspecProvider (forwarded to createFormEngine)
- Add useLocale hook: activeLocale, availableLocales, direction, setLocale, loadLocale
- Add useExternalValidation hook: inject/clear server-side validation results
- Expand useForm.submit() to accept full SubmitOptions {id, author, subject, mode}
- Export SubmitOptions, UseLocaleResult, UseExternalValidationResult types

63 unit tests + 14 E2E tests — all passing. Full roadmap (v0.2–v0.4) complete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the Swift ↔ JS bridge message types with flat JSON encoding.
EngineCommand encodes as flat objects with a type discriminator (not Swift
default enum encoding). EngineEvent decodes from the same flat shape.
Includes FieldStatePatch and FormStatePatch with touched absent from patch
(managed Swift-side only). 31 tests covering all command cases and all
event types including the touched-ignored pass-through.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements reactive state containers for SwiftUI binding:
- FieldState: identity (stable), presentation, state, options, validation, touch tracking. Holds weak FieldStateDelegate for setValue/touch delegation to FormspecEngine (Task 9).
- FormState: form-level title, description, isValid, ValidationSummary, plus page title/description cache keyed by pageId.
- JSONValue.toAny() extension converts bridge JSONValues to native Swift types.
- FieldStatePatch.apply() updates only non-nil fields; touched is never overwritten by patches.
- 26 tests covering initial state, full/partial patch application, value type conversion (string/number/bool/null/array/object), touch idempotency, and delegation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the JavaScript side of the formspec-swift WKWebView bridge:
- bridge/dispatcher.ts: window.formspecCommand() dispatcher that handles
  all EngineCommand types, installs Preact signal effects for field/form
  state changes, and posts batched EngineEvent JSON to Swift via
  webkit.messageHandlers.formspec
- bridge/esbuild.config.mjs: esbuild bundler config that inlines the
  runtime WASM binary as base64 (self-contained, no external fetch
  required in WKWebView)
- bridge/template.html: HTML shell for the bundle
- scripts/build-bridge.sh: build script that produces
  Sources/FormspecSwift/Resources/formspec-engine.html (~2.3 MB with
  WASM inlined)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements Tasks 10 and 11: ComponentMap protocol registry, auto-renderer,
and all 8 field + 5 layout default SwiftUI components.

- ComponentMap.swift: FieldComponent/LayoutComponent protocols, ComponentMap struct
  with value-semantics replacing(field:with:)/replacing(layout:with:) API
- FormspecForm.swift: top-level SwiftUI view binding FormspecEngine to the tree
- FormspecField.swift: single-field renderer with visibility gating and fallback
- FormspecLayout.swift: recursive tree renderer dispatching by NodeCategory
- DefaultFieldComponents.swift: TextInput, NumberInput, TextArea, Checkbox,
  Select, MultiSelect, RadioGroup, DateInput (all w/ required indicator,
  error display, readonly/accessibility support, platform conditionals)
- DefaultLayoutComponents.swift: Stack, Card, Grid, Page, Wizard
  (Wizard uses page TabView on iOS/visionOS, VStack fallback on macOS)
- ComponentMapTests.swift: 19 tests covering defaults membership, counts,
  and value-semantics of replacing() — all pass

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add simple-form.definition.json and simple-form.locale.en.json fixtures
for integration testing. Add contact form smoke test to FormspecEngineTests
that simulates full initialization batch (3 fields, formState, engineReady)
then a user-typing batch and verifies state is correct throughout.

Fix FieldState.apply(patch:) to clear firstError when errors is patched to
an empty array, since nil in FieldStatePatch means "no change" not "clear".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update FormspecSwift.swift barrel with full module docstring covering
the WKWebView bridge architecture, Quick Start pattern (create engine,
render FormspecForm), and Hooks Only pattern for full SwiftUI control.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s on load

Root cause: FormEngine evaluates validation continuously in its
constructor, producing required-field errors immediately. The demo
components displayed field.error unconditionally and had no onBlur
handlers to track touched state.

Fix:
- Add onBlur={() => field.touch()} to all input/select/textarea elements
- Gate error display on field.touched (showError = field.error && field.touched)
- Touch all fields on submit so untouched field errors become visible
- Checkboxes touch on onChange (no blur event for checkboxes)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Global label { display: block } was forcing checkbox inline labels
  onto their own line, breaking the flex layout. Scoped block display
  to label[for] only.
- Replaced browser-default checkboxes with custom appearance: none
  styling — rounded border, primary-colored fill, white checkmark,
  focus ring consistent with text inputs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add simple-form.layout.json fixture matching the contact form definition
- Add E2EBridgeTests in a new Integration/ subdirectory exercising the full
  Swift → WKWebView → WASM → FormEngine → signals pipeline; skips gracefully
  in headless environments where WASM signal effects don't fire
- Add FormspecDemo executable target: minimal macOS SwiftUI app demonstrating
  FormspecEngine.create, auto-rendered FormspecForm, manual field inspection,
  and validation summary display
- Update Package.swift to declare the FormspecDemo executable product and target

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Components now demonstrated:
- TextInput (string), NumberInput (integer/decimal), Select (choice)
- RadioGroup (choice with radio widgetHint) — NEW
- CheckboxGroup (multiChoice), Checkbox/Toggle (boolean)
- DatePicker (date), FileUpload (attachment), Textarea (text)
- Heading, Paragraph, Divider, Alert/Banner — display nodes — NEW
- Stack, Card/Section — layout containers
- RepeatGroup (key personnel, 1-5 instances) — NEW
- WhenGuard (conditional priorGrantId)
- Submit button with touched-gated validation

Also: fix formspec-layout planner to check widgetHint on display
items (was hardcoded to 'Text' for all display items).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mikewolfd and others added 11 commits March 26, 2026 00:33
- Fix item type from "input" to "field" in definition fixtures (matches
  formspec schema)
- Add required $formspec, url, version fields to definition fixture
- Fix E2E test to assert label propagation (proven pipeline) rather than
  bind eval timing (flaky)
- Clean up debug logging from dispatcher and WebViewEngine
- E2E test now passes: Swift → WKWebView → WASM → FormEngine → signals → Swift

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Separate Kotlin package mirroring formspec-swift architecture:
hidden Android WebView bridge, Compose State<T> reactivity,
@composable component map. Same HTML bundle and message protocol.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ition

- Add x-formspec-ein extension on EIN field
- Add x-formspec-email extension on email fields (contact + personnel)
- Add x-formspec-phone-nanp extension on phone field
- Add x-formspec-url extension on website field
- Add x-formspec-currency-usd extension on annual budget
- Change budget fields (requestedAmount, matchingFunds, totalProjectCost)
  from decimal to money dataType with currency: "USD"
- Import registry JSON and pass entries to FormspecProvider via
  registryEntries prop so extension validation actually runs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Guard

Add: Android lifecycle/ViewModel guidance, background pause/resume,
renderer process recovery, @Stable/@immutable annotations, LayoutComponent
mapping, Compose version requirement, ProGuard consumer rules, Maven
coordinates, AndroidManifest.xml in structure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eact

App.tsx: add skip-nav link, <main> landmark, <form> wrapper with
type="submit", aria-describedby/aria-hidden/role="alert" on all styled
field components, autocomplete on contact fields, error summary focus
management, aria-hidden on decorative badges, text-xs on badge text.
Heading hierarchy corrected (StyledCard/StyledStack h2→h3).

node-renderer.tsx: DisplayNode Heading h3→h2 for correct H1→H2→H3
hierarchy. RepeatGroup instances get role="group" with aria-label
numbering ("Key Personnel 1 of 3"), remove buttons get aria-label.

globals.css: darken --color-input (oklch 0.895→0.74) for 3:1 border
contrast, darken --color-warning (oklch 0.68→0.55) for 4.5:1 text
contrast, add prefers-reduced-motion:reduce rule.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eview

- Page title: "Formspec React Demo" → "Community Impact Grant Application - Formspec"
- Add autocomplete="address-level1" on State select, required attr on StyledSelect
- Add aria-required on StyledRadioGroup and StyledCheckboxGroup fieldsets
- Remove role="alert" from individual field errors to prevent announcement cascade
  on bulk submission — error summary div is the single assertive announcement point
- Strengthen focus indicator: add outline alongside box-shadow (two-indicator approach),
  increase ring opacity from 25% to 40%
- Expand prefers-reduced-motion rule to universal selector (catches Tailwind transitions)
- Use full-opacity text-destructive/text-warning in error/warning summaries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The formspec-repeat-add and formspec-repeat-remove buttons were unstyled,
rendering as plain black text. Add proper button styles: primary fill for
add, destructive outline for remove, with focus-visible indicators.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Give more breathing room between fields, remove button, instance
separator, and add button so they read as distinct visual groups.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents the Add button from sitting flush against the next section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…verrides

formspec-react library:
- Ship formspec.css with standalone form styling using --formspec-* CSS
  variables (inputs, selects, textareas, checkboxes, radios, cards,
  repeat groups, display nodes, focus rings, reduced motion)
- DefaultField: add onBlur→touch(), gate errors on field.touched,
  add required attr, formspec-field class, proper fieldset/legend for
  radio/checkbox groups, inline layout for standalone checkboxes
- DefaultLayout: add formspec-card class + formspec-card-title on headings,
  treat titled Stacks as card sections
- DisplayNode: add formspec-heading, formspec-divider CSS classes

react-demo:
- Delete all 8 Styled* field components and 2 layout overrides (280 lines)
- Create theme.json with design tokens (colors, spacing, radii, font)
- main.tsx emits theme tokens as --formspec-* CSS variables on :root
- App.tsx: 460→105 lines — just app shell + submit panel, zero overrides
- globals.css: pure app-shell styling, no form control styles

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ty-lead

Critical fixes:
- C1-C4: Darken theme tokens for WCAG contrast compliance:
  color.muted #7b8087→#636970 (4.5:1+ for hint text),
  color.input #ababaf→#767a7e (3:1+ for input borders),
  color.border #e0e3e5→#a0a4a8 (3:1+ for card borders).
  Updated both theme.json tokens and formspec.css fallback values.
- C5: Add aria-required + disabled (not readOnly) on checkbox inputs

Major fixes:
- M2: Error/warning summary headings H2→H3 (inside form, below H2 parts)
- M3-M4, m7: Use disabled instead of readOnly on select/checkbox/file
  (readOnly is invalid HTML on these elements)
- M5: Add autoComplete pass-through from node.props.autoComplete
- M6: Persistent error containers with aria-live="polite" instead of
  conditional rendering (errors announced as they appear on blur)
- M7-M8: RepeatGroup focus management + live announcements:
  after add → focus first input of new instance + announce count;
  after remove → focus previous instance or add button + announce count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mikewolfd
mikewolfd merged commit 3408ba4 into main Mar 26, 2026
5 of 6 checks passed
mikewolfd added a commit that referenced this pull request Mar 26, 2026
…#16)

* feat(fel-core): export FEL identifier validation and sanitization (E1)

Adds is_valid_fel_identifier and sanitize_fel_identifier to the Rust
lexer, exposes them via WASM, and bridges through the TS engine API.
Identifiers must match [a-zA-Z_][a-zA-Z0-9_]* and not be reserved
keywords (true/false/null/let/in/if/then/else/and/or/not).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(engine): add data type taxonomy predicates (E2)

Canonical predicates for classifying Formspec data types: isNumericType,
isDateType, isChoiceType, isTextType, isBinaryType, isBooleanType.
Each type maps to exactly one category per spec S4.2.3.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): add normalizeBinds, shapesForPath, and consolidated lookups (C1, C8)

normalizeBinds merges all bind constraints for a path with item-level
initialValue/default/prePopulate into a flat record. shapesForPath finds
all shape rules targeting a path with wildcard normalization. Both are
re-exported from the queries barrel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): add drop targets, tree flattening, and selection ops (C5-C7)

- flattenDefinitionTree: depth-first walk returning flat items with path/depth/parentPath
- commonAncestor/pathsOverlap/expandSelection: dot-path algebra for multi-select
- computeDropTargets: valid DnD locations excluding dragged items and descendants

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): add shape display, optionset usage, search index, serialization (C2-C3, C9-C11)

- describeShapeConstraint: human-readable shape descriptions
- optionSetUsageCount: count fields referencing a named option set
- buildSearchIndex: flat searchable index of all items
- serializeToJSON: extract clean definition document

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(studio): replace local helpers with formspec-core imports, delete originals

Deleted tree-helpers.ts, selection-helpers.ts, humanize.ts from studio lib.
Functions that serve the component tree (not definition-level queries) were
consolidated into field-helpers.ts. Updated 14 consumer files and 4 test
files. Build and all 825 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mikewolfd
mikewolfd deleted the feat/react-feature-complete branch March 27, 2026 00:06
mikewolfd added a commit that referenced this pull request Apr 29, 2026
…le landed

Wave 21 (`trellis/COMPLETED.md`) closes the last open sub-row in PLN-0312
(Trellis foundational crypto execution bundle): ADR 0005 crypto-erasure
evidence Stages 2-5 landed in the 9-commit train `c13282f`..`1b00a9a`.
Wave 16 (Rust HPKE wrap/unwrap) and Wave 17 (ADR 0006 key-class taxonomy
+ HPKE duplicate-ephemeral lint R17) had already landed; this update
flips PLN-0312 status from `Partial` → `Done`, replaces the "Remaining
ADR 0005 sub-row" prose with a full inventory of the three landing
waves, and points the source-pointer at `trellis/COMPLETED.md` Wave 16
+ 17 + 21 entries.

Bundled in the same hunk: PLN-0140 lead-value prose tightened; PLN-0311
source-pointer reflects the Trellis TODO #16#11 Wave-15 renumber
(both pre-existing in working tree, commit-bundled for narrative
coherence with the Wave 21 close).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant