Skip to content

Design: define, don't assign — untrusted keys are stored with define semantics, never through the __proto__ setter #2953

Closed
4 of 4 issues completed

Description

@dezren39

Summary

One rule for the write side of the prototype family: a value keyed by an externally-spellable name is stored with define semantics, never assignment semantics. Assignment (obj[key] = v, obj[key] ??= …, dst[k] = src[k] copy loops, Object.assign(dst, src)) goes through [[Set]], and for exactly one spelling — __proto__ — [[Set]] invokes the Object.prototype setter instead of creating an own property: a non-object value is silently ignored, an object value replaces the container's prototype. Three filed instances in three surfaces prove the class; this design adopts them as its phases and closes with an inventory audit.

This is the write-side counterpart of #2792 (read-side own-key lookups), whose design doc explicitly scoped this class out ("a different bug class with a different fix… a future design decides") and whose pass-23 assessment recorded the trigger: originate when a second write-side instance files after #2851. There are now three.

Design doc: files/designs/code-proto-write-side.md in the keep-the-four-repos-healthy goal workspace (problem, evidence, alternatives, risks, open questions).

The instances (audited at origin/main 106b0571)

Decisions recorded

  1. Preserve, don't reject. The key is data: a __proto__ field / setup name / serviceId is stored, listed, exported and re-read like any other key. (aggregator(cost-explorer): a setup named '__proto__' is silently never saved — save/import assign into a plain object, so the name becomes the store's prototype instead of an entry #2851's acceptance permits visible rejection for setup names; this design records preserve so all surfaces behave identically and aggregator(snapshots): SnapshotStore groups by serviceId into plain objects — a '__proto__'/'constructor'/'toString' serviceId crashes byService (blanking all snapshot history) and pollutes Object.prototype during compact #2941's already-stored records become readable instead of stranded. Validation-side rejection is also not the fix — aggregator(snapshots): SnapshotStore groups by serviceId into plain objects — a '__proto__'/'constructor'/'toString' serviceId crashes byService (blanking all snapshot history) and pollutes Object.prototype during compact #2941's acceptance already states the grouping fix must not depend on it.)
  2. The prototype must not move — asserted as a test property: after any covered write, the container's prototype is unchanged (or null by design) and Object.prototype gains no own properties.
  3. Accumulators are construction, not lookup. Grouping/counting sites are fixed by changing the container (Map, converted at the boundary via Object.fromEntries/spread), not by hasOwn guards — that is the boundary with design(own-key lookups): a plain object is not a map for untrusted keys — one own-key rule for every dynamic lookup #2792. (byService()'s crash is a ??= read, but its fix is container construction, so it lives here; design(own-key lookups): a plain object is not a map for untrusted keys — one own-key rule for every dynamic lookup #2792's Phase 3 audit should disposition it as covered by this design.)
  4. One probe matrix everywhere: __proto__ with a string value and an object value (the setter treats them differently), constructor, toString, an ordinary key — asserting own-property presence, JSON round-trip preservation, and decision 2.

Phased breakdown

Phases 1–2 adopt the origin issues (no parallels — they carry explicit acceptance with verified probes and queued triage packets). Related, deliberately NOT adopted: today's read-side filings #2920 / #2919 (id/web) belong to #2792's Phase 3 audit — their fix is a read guard.

Open questions (stay in the design doc, not filed)


Filed by the repo design architect (pass 24, 2026-10-31) for developing-today/code. Implementation phases are the linked sub-issues; phases with open questions stayed in the design doc.

Acceptance criteria

Activity

  1. added
    priority/normalOrdinary priority (the default)
    epicMeta-issue grouping multiple items (org Project Type=Epic)
    kind/designA design proposal (design-architect): multi-step, phased, decision-bearing
    on Oct 31, 2026
  2. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — design-architect pass 24 (2026-10-31): filed this design this pass. It is the write-side counterpart of #2792, whose doc scoped this class out to a future design; pass 23's recorded trigger (a second write-side instance after #2851) is met by #2828 + #2941. Phases adopted as sub-issues (verified in this issue's sub_issues list: 2941, 2851, 2828, 2954): Phase 1 = #2941, Phase 2 = #2828 + #2851, Phase 3 = #2954 (new, gated on Phases 1–2). Decisions recorded in the body: preserve-don't-reject; prototype-must-not-move as an asserted test property; accumulators fixed by container construction (Map), not hasOwn. Design doc: files/designs/code-proto-write-side.md in the keep-the-four-repos-healthy goal workspace; register entry added (status: proposed). No user/review — the origins carry explicit acceptance and the rule generalises in-tree idioms (the aggregator's own Map groupings; record's JSON path).

  3. added
    effort/lLarge: multi-day or open-ended; expect staged check-ins
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 31, 2026
  4. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    Intake review

  5. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Agent note (program tending, 2026-11-01): child progress — sub-issue #2851 CLOSED 2026-11-01T10:28:28Z (parent link REST-verified this run). This epic's last comment (2026-10-31T06:25Z) predates the closure, so the landing is recorded here. Progress note only — not a completion proposal.

  6. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    Program tending — sub-issue progress (2026-11-02)

    Closed since this thread's last activity: #2828 (closed 2026-11-02T07:02Z).

    Recorded for the parent rollup; this is a progress note, not a completion proposal — the parent stays open on its remaining children.

  7. added a commit that references this issue on Nov 3, 2026
  8. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    state: done — design #2953 closed by its Phase 3 audit
    task 7683 (lane code-j), Phase 3 = #2954. All three phases have landed:

    Contract statement (rule + probe matrix) is now in pm-stack/aggregator/README.md and the bin/record.mjs header.

    Remaining follow-up, named per this design's own open question (deep-merge helpers: report, don't pre-decide): #4957 — deepMergeProviders in bin/provider-catalog-sync.mjs, a recursive assignment-semantics merge over runtime-overlay config keys, with the #2953 probe matrix as its acceptance. It is the only (b) site not fixed inside Phase 3, and it is filed and linked from #2954 — nothing else from the sweep is outstanding.

    Closing this design issue as complete with #4957 named as its remainder.

  9. added
    queue/queuedHas a queued task in the Supabase agent queue
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 3, 2026
  10. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: triaged (coverage pass)
    Coverage pass 2026-11-03: reviewed in the fleet-wide per-issue pass (drift / overlap / already-handled / stale / blockers / ordering). Verdict: still live; queued in repo-health-code-pilot as triage at priority 40 (priority/normal). If this is already handled at current main, the triage closes it with evidence; if it needs splitting, the triage files the split.
    Found via: coverage pass 2026-11-03

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

effort/lLarge: multi-day or open-ended; expect staged check-insepicMeta-issue grouping multiple items (org Project Type=Epic)kind/designA design proposal (design-architect): multi-step, phased, decision-bearingmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)queue/queuedHas a queued task in the Supabase agent queuesource/design-architectFiled by the repo design architect jobstatus/needs-triageNot yet triaged; needs a first looktask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions