Repository navigation
Deck reader: case-insensitive node names and .equiv pseudonyms (documented) #156
Description
Problem
User's Guide §1.1: "Everything is case INsensitive". §1.3: "Uppercase is converted to lower case". The guide's own §1.2 deck defines N3 and later writes .equiv nin n3. Our reader matches node names case-sensitively, a recorded choice in docs/fasthenry-compat.md ("Node/element names case-sensitive"), so that deck fails with unknown node 'n3'. 2 of the 19 runnable example decks stop here.
Measured (self-authored decks):
- Names are case-insensitive everywhere: node definitions, segment endpoints,
.external,.equiv, and in-plane node names. Output names are lowercased. Definingn1and thenN1is a duplicate-definition error. - There are no forward references. A segment endpoint,
.externalor.equivnaming a node defined on a later line is an error. This matches ours. .equivpseudonyms (documented §1.3.7: "If any of the node names are not previously defined, then they become pseudonyms for those nodes in the list which are defined"):.equiv nalias n3letsnaliasbe used in.externaland as a later segment endpoint;- an undefined name in the middle of the list works;
- all names undefined is an error ("No real nodes in equiv statement");
- defining a node after it became a pseudonym is a duplicate error.
.equivchains transitively (ours already does)..equiv n2 n2is only a warning in FastHenry. Ours errors.
Expected
- All modes (documented): fold node names to lowercase for matching, so
N1/n1becomes a duplicate-name error, never two nodes. Output keeps the deck's first spelling or lowercases it; pick one and document it. Implement.equivpseudonym creation for not-yet-defined names, with the "no defined node in .equiv" error when none exist. Keep the no-forward-reference errors. --fasthenry-compatonly:.equiv x xbecomes a warning. Native mode: error, as now.- Note that this reverses the compat-table decision. A native deck that relied on
N1≠n1now gets an explicit duplicate error rather than a silent reinterpretation.
Acceptance criteria
- Mixed-case references resolve in every position (segment ends,
.external,.equiv, in-plane nodes). - Case-only duplicates are an error.
- Pseudonym tests: alias first, alias last, alias in the middle, all-undefined error, define-after-alias error.
- Compat:
.equiv x xwarns. - Compat-table "case-sensitive" row reversed, with rationale.
Clean-room provenance
Specified from the public FastHenry User's Guide / nonuniform ground-plane manual and from black-box runs of a locally built FastHenry on self-authored decks (2026-10-05, research for #74). No FastHenry source or distributed example deck was consulted or quoted; implement from this spec and the public docs only (CONTRIBUTING.md). Found by tools/fasthenry_compare.py over the FastHenry example corpus (#74).
Part of #76.
Implementation Guidance
Curation refresh, 2026-10-06: implementation is already proposed in PR #164. Review that PR against this scope rather than starting a second implementation. Its reported test results are not independently re-run by this curation pass.
Centralize node-name normalization in the name registry so declarations and references share the same rule. Pseudonyms should point to existing node slots without creating geometry. Choose a defined node from anywhere in an .equiv list before processing undefined names; retain transitive alias resolution and slot compaction. A compat self-equivalence warning must not prevent later entries in the same list from being joined. Include the named nodes in contact equiv_rect and contact connection when checking in-plane name handling. Document the chosen output spelling; explicit port labels and this reader's extension group= names are outside this node-name change.
Affected Files
fasterhenry-cli/src/inp.rs— name registry,.equivparsing, node reference/definition paths, parser documentation and unit tests.fasterhenry-cli/src/cli.rs— keep compatibility help consistent with the self-equivalence warning behavior.docs/fasthenry-compat.md— revise node-name and.equivrows and explain the documented behavior replacing the previous decision.CHANGELOG.md— record the observable parser compatibility changes.
Test Plan
- Parser tests compare mixed-case and lowercase self-authored decks in native and compat modes, including segment endpoints,
.external,.equiv, ordinary in-plane nodes and named contact-area nodes. - Check ordinary/in-plane case-only collisions and define-after-pseudonym collisions produce line-numbered errors.
- Exercise alias first/last/middle, mixed-case aliases, all-undefined
.equiv, and transitive chains; verify aliases add no extra geometry and yield the same solved deck as direct node references. - Preserve unknown-node errors for segment and port forward references;
.equivpseudonyms must not permit an all-undefined list. - Native self-equivalence still errors; compat emits a warning and continues joining later names in the list, including names already joined transitively.
- Verify default port-label spelling matches the documented choice and explicit labels retain their spelling.
- Run
cargo test -p fasterhenry-cli,cargo fmt --all -- --check, andcargo clippy --workspace --all-targets -- -D warningsusing the repository's isolated Cargo target workflow. Use only self-authored fixtures perCONTRIBUTING.md.
Verified corrections
- 2026-10-06, verified against
origin/main@e5d924abb67d149f1f3a066678078c36af2c3e7e(source inspection ofNames::{define,lookup},Parser::apply_equiv,apply_external,apply_node, andapply_plane_corner_forminfasterhenry-cli/src/inp.rs): registry keys currently use exact strings;.equivlooks up every name and rejects equal resolved slots. Ordinary and corner-form in-plane declarations both reachNames::define. These paths establish the parser locations above; they are not a fresh measurement of FastHenry behavior. - 2026-10-06, verified via GitHub REST (
GET /repos/2AMLogic/fasterhenry/pulls/164and/pulls/164/files): PR Deck reader: case-insensitive node names and .equiv pseudonyms #164 is open at head2b47666f163c387ca5ffdd8478fbb27f70d7278c, references this issue, and modifies the four files above. All four exist on the fetched build base (git ls-tree -r origin/main --name-only). Duplicate checking reported this related PR; the open backlog has no eligible sibling micro-issue to consolidate.
Activity
loom-fleet-dispatch commentedon Oct 6, 2026
This issue's
loom:buildingclaim was acquired by sweepsweep-issue-156-1791246242on hosthost-d9142cf3at 2026-10-06T00:24:04.848185+00:00. This comment is a lease record (Issue #6179, Epic #6165) — its liveness signal is this comment's own forge-assignedupdated_at, never a timestamp embedded in this text. Seedefaults/docs/lease-record.mdfor the format contract this establishes, anddefaults/docs/lease-renewal.mdfor how the owning sweep keeps it fresh for the lifetime of its claim. Nothing reads this record yet (write-only, Phase 1) — a future phase will use it to decide reclamation of an abandoned claim.loom dashboard
loom-fleet-dispatch commentedon Oct 6, 2026
Curated 2026-10-06: retained the approved behavior and added Implementation Guidance, Affected Files, Test Plan, and dated source/API verification. Included named contact-area nodes in the test guidance. Related implementation PR #164 is already open; this pass did not independently run its tests. The premise gate returned out-of-scope, duplicate checking found that related PR, and all four affected paths exist on fetched origin/main. No eligible sibling scope requires consolidation. Retaining
loom:issuebecause this is a non-material planning refresh.loom dashboard
Deck reader: case-insensitive node names and .equiv pseudonyms
loom-fleet-dispatch commentedon Oct 16, 2026
This issue is being worked by an in-session
/loom:sweeprunsweep-insession-s757588-11385491on hosthost-6d9efa8b, published at 2026-10-16T00:22:18Z. This comment is a lease record (Issue #6320, Epic #6165) in the identical formatloom-daemon's dispatch-time write uses (#6179) — its liveness signal is this comment's own forge-assignedupdated_at, never the timestamp in this text. It is renewed bysweep-lease-renew.sh(#6180) for as long as this sweep's own process is alive, and simply ages out afterwards. Readers: the daemon's reclamation gate (#6286) refuses to reclaim this claim while the lease is fresh, dispatch-time ordering (#6287) yields to it, andsweep-lease-fence.sh(#6309) fences this sweep's own push/PR-open against it. Seedefaults/docs/lease-record.md.Merge origin/main into feature/issue-156 (Doctor: resolve conflicts)