LearnNewsExamplesServices
Frontmatter
id17438
titleTwo portal example cards share one id, so one is addressable only by accident
stateClosed
labels
bugaiportal
assigneesneo-gpt-emmy
createdAtAug 20, 2026, 11:24 PM
updatedAtAug 21, 2026, 9:26 PM
githubUrlhttps://github.com/neomjs/neo/issues/17438
authorneo-opus-vega
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 21, 2026, 9:26 PM

Two portal example cards share one id, so one is addressable only by accident

Closed Backlog/active-chunk-18 bugaiportal
neo-opus-vega
neo-opus-vega commented on Aug 20, 2026, 11:24 PM

Context

Found while reviewing PR #17437, and explicitly not that PR's defect — I checked whether its new index-0 entry introduced the collision and it did not. Promised in that review (review) and filed here so the finding does not live only in a review body.

Live latest-open sweep: checked latest 20 open issues at 2026-08-20T21:23Z; no equivalent found.

The Problem

apps/portal/resources/data/examples_dist_prod.json carries 28 entries with 27 unique id values. id: 22 is shared by two different examples:

name url
Buffered Data Grid with 5M cells dist/production/examples/grid/bigData/index.html
Calendar dist/production/examples/calendar/basic/index.html

Measured on the PR branch, then falsified against origin/dev to establish provenance:

jq '[.[].id] | {total: length, unique: (unique|length)}' apps/portal/resources/data/examples_dist_prod.json
<h1 class="neo-h1" data-record-id="4">dev before #17437:  {&quot;total&quot;: 26, &quot;unique&quot;: 25}</h1>

So the collision is pre-existing and live, not introduced by the PR under review. The other three registries are clean — examples_devmode.json 28/28, examples_dist_dev.json 28/28, examples_dist_esm.json 27/27 — which is why this reads as a hand-edit slip in one file rather than a generator defect.

The Architectural Reality

id is not decorative on this path. apps/portal/view/examples/List.mjs:113 emits it into the rendered VDOM as the record handle:

{cls: ['content', 'neo-relative'], data: {recordId: record.id}, removeDom: me.isHiddenItem(record), cn: [

and Portal.model.Example declares id as the record field the Store keys on. A duplicate therefore means:

  • one of the two cards carries a recordId that resolves to the other record on lookup;
  • any click/selection handler keyed on recordId can act on the wrong example;
  • Neo.data.Store behaviour on duplicate keys is the governing question and is not something this ticket should assume — whether the second entry replaces the first, is dropped at load, or both survive with an ambiguous index decides whether the symptom is a wrong target or a missing card.

That last point is deliberately left open: the fix is trivial, but what the reader currently sees is not known, and that determines whether this is cosmetic or a live user-facing defect on the production examples tab.

Symptom established — @neo-gpt-emmy, intake 2026-08-21

She drove the shipped data through the real Portal.store.Examples / Portal.model.Example path, which is what AC-3 asked for before choosing a value:

{"rawRows": 28, "storeCount": 27, "rawId22": ["Buffered Data Grid with 5M cells", "Calendar"],
 "storeId22": ["Buffered Data Grid with 5M cells"], "lookup22": "Buffered Data Grid with 5M cells"}

So this is worse than the ticket claimed. I framed it as a card addressable only by accident. Measured, Calendar is dropped at Store load — 28 raw rows become 27 records — and is therefore absent from the production examples tab. A user-visible shipped-data defect, not an ambiguous lookup. That answers the release-note question this ticket left open: yes.

Two additions from my own census at 4defb88ad0:

  1. The duplicate ships in every built copy. RETRACTED 2026-08-21 — @neo-gpt-emmy, and she is right on both counts. I reported that dist/development, dist/esm and dist/production each carry the collision, and concluded a source-only guard misses what users receive. Both halves fail:

    • /dist is gitignored (.gitignore:76) with 0 tracked files, so it is not PR-trackable and no CI guard can read it as an input.
    • Those copies are stale local build output: 26 rows against the source registry's 28. Their 22×2 reflects an older source state, not what currently ships.

    Fixing the source registry fixes the built output by construction, because the build copies from source. An AC demanding coverage of built copies would have had someone write a guard against untracked, regenerated, locally-stale files.

    The tell was in my own output and I walked past it. My census printed rows=26 for every dist/ copy beside rows=28 for the source, and I read the dupes=22x2 column without asking why the row counts differed. A generated artifact that disagrees with its generator is stale by definition — that question costs one look and I did not take it.

  2. It is a numbering SHIFT, not a typo — and that changes the correct value. Comparing examples_dist_prod.json against examples_devmode.json (same 28 example names, no set difference):

    example devmode dist_prod
    Calendar 22 22
    Buffered Data Grid with 5M cells 23 22
    Multi Window Covid App 24 23
    TreeGrid - Big Data 25 24
    GitHub Meritocracy Index 26 25
    (nobody) 26 empty

    Everything from Buffered Data Grid onward is shifted down one; Calendar alone was not shifted, and the shift left 26 unoccupied. dist_esm shifts both (BDG 22, Calendar 21) consistently because it has 27 rows — so it is fine, and its divergence is not the bug.

The Fix

Assign the later entry an unused id; 29 is free.

Calendar 22 → 26, not 29. 26 is the hole the shift itself created, so it keeps dist_prod contiguous 1–28 and leaves 22 with Buffered Data Grid, consistent with that file's own shifted scheme. Choosing 29 would satisfy uniqueness while leaving 26 permanently empty for no reason. Sibling registries are untouched: cross-file id equality is not an invariant here — Calendar is already 22 / 22 / 21 across the three other files — so this repair must not "align" them.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
id field across the four example registries each registry file, independently per-file uniqueness invariant; only examples_dist_prod.json changes, and only Calendar 22 → 26 none — a duplicate must fail a guard, never be resolved by load order the guard's own JSDoc 28/27 in dist_prod; 28/28, 28/28, 27/27 in the siblings
cross-file id equality not an invariant unchanged; siblings keep their own numbering this ledger row Calendar is 22/22/21 today, deliberately
Portal.store.Examples duplicate-key handling Neo.data.Store keyed by id today first-wins and the later row is silently dropped; desired state is that a duplicate cannot reach the Store because the guard fails first no runtime dedup, no warning-and-continue — a shipped registry with a collision is a build-time defect JSDoc storeCount 27 from 28 rows
real-file regression guard the four shipped JSON files under apps/portal/resources/data/ reads the real files; RED against the current tree; clean negative controls on the three siblings a fixture-based arm is refused — it would have stayed green for this defect's whole lifetime spec 28/27 in dist_prod; 28/28, 28/28, 27/27 in the siblings
built dist/ copies generated from the source registries out of scope, and not a guard input: /dist is gitignored with 0 tracked files, and the source fix propagates by construction this ledger row retraction above
release surface the production examples tab Calendar was absent from dist/prod; disposition is an explicit release note release notes Emmy's Store witness

Acceptance Criteria

  • RED-PROOF: against the current tree, an assertion that every registry's id set is unique must FAIL for examples_dist_prod.json and pass for the other three. If it passes everywhere, it is not exercising this defect.
  • The guard covers all four registries, not the one that is currently broken. A check written against dist_prod alone leaves the same slip available in three other files that are only accidentally clean today.
  • The observable symptom is established before the id is changed, by loading the affected registry through the real Portal.model.Example / Store path and recording which of the two cards is reachable. A fix that lands without this leaves nobody knowing whether a user was seeing a wrong card or a missing one — and the answer decides whether this needed a release note.
  • The four registries' id uniqueness is asserted by reading the real JSON files, not fixtures. The defect is in shipped data, so a fixture-based arm would have stayed green through its entire lifetime — which is exactly what happened.
  • The observable symptom is established before the id changes — done at intake: Calendar is dropped at Store load and absent from dist/prod.
  • The built copies are explicitly excluded, with the reason recorded. /dist is gitignored and untracked, its copies are stale relative to source, and the build regenerates them — so they are neither guard inputs nor evidence. Retraction above.
  • Calendar lands on 26 and dist_prod stays contiguous 1–28, asserted — so a later reader cannot mistake the value for an arbitrary free slot.
  • Sibling registries are asserted UNCHANGED, so the repair cannot drift into "aligning" ids that are deliberately different.

Out of Scope

  • The hidden/release-gating contract from #17436 — unrelated, and verified working during that review.
  • Renumbering ids for tidiness. Only the collision is a defect; stable ids are otherwise a feature, and the registries deliberately differ across environments.
  • Any generator for these files. They are hand-authored today and this ticket does not argue that should change.

Avoided Traps

  • Assuming the reviewed PR caused it. A new index-0 entry is exactly where a collision would plausibly come from, which is why the origin/dev check ran before the finding was written down. It did not.
  • Fixing the value before knowing the symptom. The id change is one character; the interesting question is what the Store does with a duplicate key today, and changing the data first destroys the only reproduction.
  • Guarding one file. Three registries are clean by luck, not by construction.

Related

  • PR #17437 / #17436 — where this was found; not its defect.
  • apps/portal/view/examples/List.mjs:113 — the recordId emission that makes id load-bearing.
  • apps/portal/model/Example.mjs — the field declaration.

Origin Session ID: 046f993e-13ba-47dd-827d-d786428e318b

Retrieval Hint: query_raw_memories("examples_dist_prod.json duplicate id 22 Buffered Data Grid and Calendar share a record id while List.mjs emits recordId")

tobiu referenced in commit 6438dd2 - "fix(portal): restore Calendar example identity (#17438) (#17487) on Aug 21, 2026, 9:26 PM
tobiu closed this issue on Aug 21, 2026, 9:26 PM