Closed general

Quick actions resolved part files by a weaker rule than the indexer and ignored explicit opt-outs

Colby Knox · 1mo ago ·closed by Colby Knox

Reported by Ray (relayed by Colby; filing so the fix has an address).

The serve-time quick-action builder (lib/quick-actions.js, extracted from server.js kicadActions) answered "which file is the part" with a weaker rule than the indexer: if the indexed path was falsy or missing from the tree, it fell back to the FIRST tree entry matching the extension. Three defects rode on that:

  1. An explicit opt-out was not honored. parts.footprint: false stores null at index time; serve-time treated null as "go find one" and would stage a file the publisher opted out of. No live victim today (all 3 opt-out pages opt out of datasheet only), but a live logic bug.
  2. Latent ambiguity: 4 of 952 component pages ship multiple top-level footprints (ams1117: so8/sot223/to252 declared sot223, first-match so8; lan7800: qfn48-6x6 vs 7x7, same pin count, wrong body would look plausible and simply not fit). All 4 declare correctly, so the fallback never fired, but a stale or renamed declared path would have degraded them to first-match.
  3. The same question answered by two different rules, and the weaker one is the one that writes to a reader's disk and opens their editor.

Fix: the indexer's #145 rule (render/ never, root . first, then any root file, then subfolders) is now exported as pickPartFile and serve-time reuses it. A null indexed path renders NO action (opt-out and no-confident-candidate both mean the indexer declined; the button never hunts on its own). The only serve-time re-pick is the stale case (declared file renamed or removed since indexing), and it runs the indexer's own rule. The footprint's 3D model attaches from the indexed step_path, with the #92 name-match (which abstains, never guesses) as the only fallback.

Same principle as adom/adom-bridge#92: a guess that produces a plausible artifact is worse than an error.

2 Replies

Colby Knox · 1mo ago

Fixed and on prod. Confirmed on all three counts before touching anything: the extraction into lib/quick-actions.js carried the weak resolve verbatim, and it gates the Fusion action too, so the surface was wider than the kicadActions you quoted.

What shipped:

  • The indexer's #145 rule is now a shared export (pickPartFile in lib/index.js) and serve-time calls it instead of reimplementing. One rule, one answer.
  • A null indexed path renders NO action. Opt-out (parts.footprint: false) and no-confident-candidate both mean the indexer declined, and the button never hunts on its own.
  • The stale case (declared file renamed or removed since indexing) is the only serve-time re-pick, and it runs the indexer's own rule: render/ never wins, slug-named root first, then root, then subfolders. Only-render-candidates means no action.
  • The footprint's 3D model attaches from the indexed step_path; the only fallback is the #92 name-match, which abstains on ambiguity rather than guessing.

Verified: 7 new unit cases pinning exactly your scenarios (opt-out with a matching file in-tree, stale metadata over a render/ decoy, the ams1117 shape); a staging throwaway with so8/sot223/to252 in-tree served only the declared sot223, then flipping to footprint: false removed the button while the symbol action stayed; and on prod, ams1117 serves ams1117-sot223.kicad_mod and lan7800 serves the 7x7, with so8 and 6x6 appearing nowhere.

Thanks Ray. Same lesson as adom/adom-bridge#92, and this one was mine: the extraction should have adopted the indexer's rule instead of preserving the shortcut.

Colby Knox · 1mo ago

Shipped to prod, credit to Ray for the report. Serve-time now answers "which file is the part" with the indexer's own rule instead of a first-match reimplementation:

  • The indexed path in component_metadata is the answer. A null there (opt-out via parts.footprint: false, or no confident candidate at index time) renders NO action; the button never hunts the tree on its own.
  • The stale case (declared file renamed or removed since indexing) re-picks via pickPartFile, the exported #145 rule (render/ never, root slug-named first, then any root file, then subfolders), shared by the indexer and the action builder so the two can no longer diverge.
  • The footprint's 3D model attaches from the indexed step_path, with the #92 name-match (mpn, slug, lone candidate; abstains on ambiguity) as the only fallback.

Verified on staging with a throwaway opt-out page (parts.footprint: false plus a stray .kicad_mod in-tree: symbol action renders, no footprint action, the stray file is never offered) and on prod against ams1117 (footprint action stages the declared ams1117-sot223.kicad_mod). Unit tests pin the opt-out, the stale re-pick, render/ exclusion, the declared-wins case, and step abstention.

Log in to reply.