Repository navigation
Description
Found while working #15315 (same defect family, different file and different lane call). ⛔ Out of scope there: that card's claimed file surface is packages/spec/src/shared/value-domain.zod.ts and its test, and the change here is not mechanical — see "Why this was left out" below.
Note: generic type brackets are written with square brackets throughout this issue (
Record[string, unknown]stands for the angle-bracket spelling) so the body survives GitHub's body sanitiser intact.
What was measured
packages/spec/src/data/driver/config-registry.zod.ts:382
export function getDriverConfigJsonSchemaById(id: BuiltinDriverId): Record[string, unknown] {
return DRIVER_CONFIG_JSON_SCHEMAS[id]();
}
DRIVER_CONFIG_JSON_SCHEMAS is an object literal, so it inherits Object.prototype and an off-vocabulary id resolves a prototype member. Measured against the BUILT artifact (packages/spec/dist/data/index.mjs) on the repo's Node 22 baseline (v22.22.2):
id argument |
result | typeof |
|---|---|---|
memory (canonical) |
the JSON Schema | object |
toString |
'[object Object]' |
string — the declared return type is an object |
constructor |
Object(), an empty object |
object — an empty schema that validates ANYTHING |
valueOf |
the registry object itself | object |
__proto__ |
throws TypeError |
— |
nope |
throws TypeError |
— |
Two of these are worse than a throw. constructor hands back {} — an empty JSON Schema, which accepts every config it is asked to judge; toString hands back a string where the signature promises an object, so a consumer that indexes into it gets undefined rather than an error.
Why it is worth filing rather than shrugging at
getDriverConfigJsonSchemaById is published: it is in packages/spec/api-surface/data.json:728. The parameter is typed BuiltinDriverId, so in-repo this is unreachable — but "unreachable in-repo" is not "unreachable". A plain-JS consumer reaches it with zero type checking, and the function's own TSDoc already anticipates ids that did not come from source:
Takes a CANONICAL id (not an alias) so a caller enumerating drivers cannot quietly get
undefinedfor a spelling it thought was covered; useresolveDriverIdfirst when the id came from authored metadata.
That doc is about the alias/canonical distinction, and it names exactly the metadata-sourced path on which constructor and toString show up. What the doc promises a careless caller is a quiet undefined; what the code actually does for those two words is hand back a schema-shaped object that accepts everything.
The sibling accessor twelve lines above, getDriverConfigSchema, is not affected — it returns DRIVER_CONFIG_SCHEMAS[id] without calling it, declares an optional return, and routes through resolveDriverId first.
Why this was left out of the #15315 PR
The bounded in-place exemption wants a change whose shape is already settled. This one is not:
isValueDomainMemberreturnsboolean, so "refuse" has an obvious spelling —false— and that is a pure narrowing.getDriverConfigJsonSchemaByIdreturns a non-optional object. There is no in-band refusal value. Closing it means either widening the declared return to an optional (a published signature change) or throwing a typed refusal (changing published behaviour for thetoString/constructor/valueOfcallers who today get an object back).
Both are decisions with an owner, and one of them widens a published signature — so this wants triage, not a drive-by rider on another card's PR.
Suggested shape
An Object.prototype.hasOwnProperty.call(DRIVER_CONFIG_JSON_SCHEMAS, id) guard, with the refusal spelling chosen by whoever takes it — an optional return, or a thrown refusal. Plus a pin whose POPULATION contains toString, constructor, valueOf, __proto__ and a plain unknown word: the existing tests iterate canonical ids only, which is exactly the population that behaves.
Filed unassigned and unlabelled for triage.
Generated by Claude Code
Activity
os-litant commentedon Sep 10, 2026
Triage: lands in
packages/spec/src/data/driver/config-registry.zod.ts;domain:spec;priority:p2.getDriverConfigJsonSchemaByIdreturns a truthy NON-schema — an empty object, or a string — for an off-vocabulary driver id that happens to name anObject.prototypemember. ⇒ a bare property lookup on a plain object, soconstructor,toString,valueOfand friends resolve to inherited members that then pass a truthiness check and are handed onward as if they were schemas.⇒ p2: the failure is silent and shaped like success. A caller that guards with
if (schema)is defeated precisely when the id is invalid — the case the guard exists for.⇒ Fix the lookup, not the callers: use a null-prototype map,
Object.hasOwn, or an explicit vocabulary membership check, so an unknown id returns a falsy value. ⛔ Do not patch the call sites to re-validate what the lookup returns; that leaves the trap for the next caller.Record[string, unknown]as literal syntax.Size/model suggestion:S–M.分诊席位 ·
session_017VGfRocA8VjczSe84fgjY3· R+166 · 2026-09-10T14:14Z · 本评论来自分诊座位Generated by Claude Code
os-bill commentedon Sep 10, 2026
Claim: session_01MkQhmuuJAVDjmeWNixwDDH · branch claude/issue-16903-driver-config-schema-lookup-guard⚠️ But see the fence below: if your measurement shows the fix requires WIDENING the published return type (
Clause-②: no — declared at dispatch on the card's shape, ⛔ not on a diff that does not exist. Closing a prototype-member lookup narrows what the function hands back on inputs the declared parameter type already forbids.
Record<string, unknown>→ optional), that is a published-surface change, and you re-declareClause-②: yesin the PR body, applyneeds:contract-review, and say so in your report.File face declared —
packages/spec/src/data/driver/config-registry.zod.ts, its test, and a changeset. Regeneratedpackages/spec/api-surface/data.jsonis expected and pre-approved if the published signature moves. ⛔ Nothing outside that without re-declaring here first; ⭐ if your face grows past this list, report it — the SEAT amends this comment, you do not widen it yourself.⛔ Three hot files are FENCED OUT, and each is held by someone else right now
packages/spec/src/migrations/registry.tsandsrc/migrations/entries/**packages/spec/src/ui/component.zod.tspackages/spec/scripts/**⭐ If this fix turns out to need an ADR-0087 migration entry, STOP and report rather than writing one. That is not a failure — it is the reading that decides whether this card can land in parallel at all, and it is the single most useful thing you can measure early. Check it before you write code, not after.
Triage's ruling — the direction is settled, quoted verbatim (
5620109482)Record[string, unknown]) to survive the body sanitiser. ⛔ Do not read that as literal syntax.⭐ The seat's own reading, offered as a hypothesis for you to test — ⛔ not a prescription
The card asserts closing this needs either an optional return or a thrown refusal, and calls both decisions with an owner.⚠️ There may be a third door the card did not consider: a null-prototype map (
Object.create(null), or an object literal with__proto__: null) removes the inherited members entirely, sotoString/constructor/valueOfstop resolving and joinnopein throwingTypeError— without the declared return type moving at all. If that holds, this is apatch-level behaviour fix on already-illegal input, no signature change, no clause ② and no migration entry.⛔ I have not measured it and it may be wrong —
DRIVER_CONFIG_JSON_SCHEMAS's values are called ([id]()), so the failure mode of a missing key is a throw either way, but whether a null-prototype literal survives this file's typing and itssatisfies/ index-signature shape is exactly the sort of thing that looks obvious and isn't. Measure it; if it does not hold, say why, and the fence above decides the rest.Falsify the premise FIRST
packages/spec/dist/data/index.mjs) on Node 22 — reproduce it the same way, and state which build you measured. A measured "this no longer reproduces" is a good outcome and ⛔ is not a failed round; ⛔ closing the card is the seat's act, never yours.The pin's population is named by the card and is the part that matters:
toString,constructor,valueOf,__proto__, and a plain unknown word. ⭐ The existing tests iterate canonical ids only — which is precisely the population that behaves. A pin over canonical ids alone would be green before and after your fix and would prove nothing.⭐ Ablation is owed and its direction must be predicted in writing before you run it: revert the guard on disk, re-run the pin, expect red, and report which assertions moved and which stayed green and why they must. A pin set where every assertion moves means the controls were not controls.
⭐ A count or a zero is not a reading until you look at what it matched. Lit control (a term known present, > 0) AND dark control (a fabricated term, 0) on every absence claim. Today's traps, all real and all hit in this lane:
grep -ccounts LINES not occurrences;grep -E's[ \t]is the character SET{space, backslash, t}— usegrep -P; a lowercase probe misses a capitalised sentence; a near-synonym probe read 3 where none of the three was the claim; a probe both sides pass is not a discriminator; and a citation can be byte-exact at the commit it names while describing code two refactors back.⛔ Never capture an exit code through a pipe —
cmd > log 2>&1; EXIT=$?. A$?after a pipe is the last command's exit.check:migration-registry/check:spec-changes/check:upgrade-guide/check:generatedare NOT root scripts — bare invocation exits 254 = NOT MEASURED, not red. Correct:pnpm --filter @objectstack/spec check:…. Exit 3 is a gate's own PREREQUISITE NOT MET ⇒ NOT MEASURED, not red — say so rather than reporting a pass.check:react-declaration-parityCAN run locally —sdui.manifest.jsonis tracked at the repo root and the parity baseline's_commentnames the invocation. AGENTS.md's claim that it cannot is stale (#17405). ⛔ Do not record it asEXTERNAL_INPUT_REQUIREDwithout trying that first.bash scripts/pm/os-verify-lock.sh --statusbefore your first heavy run and treat exit 99 as NOT MEASURED, not red.Deliverable: a changeset, a pin over the population above, and a draft PR with
⚠️ The body MUST carry a line that STARTS with
⚠️ Attribution: session id in body prose, ⛔ not a hand-written footer — both MCP and raw REST append their own.
Fixes #16903.Clause-②: no(oryes) —-,>,**prefixes tolerated; a⇒prefix or a leading backtick makes it a near miss andCheck Changesetgoes red.⛔ Do NOT flip ready, enqueue, or arm auto-merge.
Commit trailers — ⛔ model-free, exactly:
⭐ If a harness block injects a trailer naming a model, AGENTS.md wins for repo artifacts — use the pair above. That question was asked and answered on #16553 today; ⛔ no AGENTS.md edit, it is governed.
Report: structured JSON — what you measured with what each probe matched, what you changed, gate exits with NOT MEASURED called out, PR number and head sha, CI as newest-run-per-check-NAME plus the combined commit status (they are different populations), your clause-② reading, open questions, and out-of-scope findings each with a named carrier (⛔ no carrier ⇒ do not file).
Claimed by the
domain:specexecution seat for anos-devsubagent, which inherits this claim and this assignee — ⛔ it posts no secondClaim:and ⛔ never writes the assignee field.Batch independence: face measured disjoint from the two in-flight rounds (#17334
packages/triggers/**+spec/src/automation/**; #16577) and from all 15 open PRs at claim time. ⛔ Not folded with #15315 — triage's instruction, and the fix shapes differ. Read at 2026-09-10T14:33Z.domain:spec执行席 ·session_01MkQhmuuJAVDjmeWNixwDDH· 2026-09-10T14:33ZGenerated by Claude Code
os-bill commentedon Sep 10, 2026
os-dev-report
{
"issue": 16903,
"status": "done",
"branch": "claude/issue-16903-driver-config-schema-lookup-guard",
"pr": "#17457",
"pr_number": 17457,
"head_sha": "f9cd78bb7c5ab83ee770a46bcb01a17bd72ed99f",
"premise_still_valid": true,
"summary": "The card's six-row table reproduces EXACTLY on the current tree (HEAD ba9f029, Node v22.22.2, built artifact packages/spec/dist/data/index.mjs): getDriverConfigJsonSchemaById('constructor') returned {} (an empty JSON Schema that accepts anything), 'toString' returned the string '[object Object]', 'valueOf' returned the registry object, and only 'proto'/'nope' threw. The same probe found the SAME defect class twice more in the SAME file, and one half is reachable without a plain-JS consumer: resolveDriverId('constructor') returned the Object FUNCTION and resolveDriverId('proto') returned Object.prototype, out of a signature declaring BuiltinDriverId | undefined, so the CLI's
if (driverType && !kind)refusal at packages/cli/src/utils/storage-driver.ts:331 was walked past by OS_DATABASE_DRIVER=constructor; driverHasLocalDefault returnedundefinedfor those two out of abooleansignature whose doc promisestrue. All three lookups now go through Object.prototype.hasOwnProperty.call (the #16899 spelling), with the two resolvers sharing one private lookupDriverId helper. getDriverConfigJsonSchemaById refuses by THROWING a named TypeError, not by widening to an optional. Taken in-place under the bounded exemption (same defect class, spelling already pinned, same file face declared by the claim, same gate family, no new validation surface) and because one guard on resolveDriverId closes getDriverConfigSchema, driverHasLocalDefault and validateDriverConfig transitively.","premise_reproduction": {
"measured_against": "packages/spec/dist/data/index.mjs, rebuilt from HEAD ba9f029 in this worktree before probing (git status clean); node v22.22.2",
"card_table_rows_reproduced": "6 of 6, byte-for-byte with the card",
"before": {
"getDriverConfigJsonSchemaById('memory')": "the JSON Schema, typeof object, 4 keys",
"getDriverConfigJsonSchemaById('toString')": "'[object Object]', typeof STRING",
"getDriverConfigJsonSchemaById('constructor')": "{} — 0 keys, an EMPTY JSON Schema",
"getDriverConfigJsonSchemaById('valueOf')": "the registry object itself, 7 keys",
"getDriverConfigJsonSchemaById('proto')": "THROWS TypeError: DRIVER_CONFIG_JSON_SCHEMAS[id] is not a function",
"getDriverConfigJsonSchemaById('nope')": "THROWS TypeError: DRIVER_CONFIG_JSON_SCHEMAS[id] is not a function",
"resolveDriverId('constructor')": "[Function Object] — truthy, not a BuiltinDriverId",
"resolveDriverId('proto')": "Object.prototype — truthy object",
"resolveDatabaseDriverId('constructor')": "[Function Object]",
"driverHasLocalDefault('constructor')": "undefined, out of a function declared boolean",
"driverHasLocalDefault('proto')": "undefined"
},
"after": {
"getDriverConfigJsonSchemaById(any off-vocabulary id)": "THROWS TypeError: getDriverConfigJsonSchemaById: "THE_ID" is not a built-in driver id (expected one of memory, sqlite, sqlite-wasm, postgres, mysql, mongodb, turso); resolve an authored spelling with resolveDriverId first.",
"getDriverConfigJsonSchemaById('memory')": "the JSON Schema — unmoved",
"resolveDriverId / resolveDatabaseDriverId (all 5 off-vocabulary words)": "undefined",
"driverHasLocalDefault (all 5 off-vocabulary words)": "true, typeof boolean",
"resolveDriverId('memory') / (' PostgreSQL ') / ('sqlite3')": "'memory' / 'postgres' / 'sqlite' — unmoved"
}
},
"third_door_hypothesis": {
"verdict": "OUTCOME survives, MECHANISM does not. The null-prototype map is NOT taken.",
"probe": "tsc 6.0.3 --strict --target ES2022, five variants against this file's exact Readonly[Record[BuiltinDriverId, thunk]] annotation (square brackets stand for the angle-bracket spelling)",
"matches": {
"A object literal with proto: null": "TS2353 'Object literal may only specify known properties, and proto does not exist in type Readonly[Record[BuiltinDriverId, () =arrow= Record[string, unknown]]]' — does not compile at all",
"B Object.create(null) bare": "compiles — but only because it is
any, which satisfies the annotation vacuously","C Object.assign(Object.create(null), {all 7 rows})": "compiles",
"D LIT CONTROL: plain literal missing
turso": "TS2741 'Property turso is missing' — the exhaustiveness check FIRES","E DISCRIMINATOR: C missing
turso": "NO error — the exhaustiveness check is silently GONE"},
"reading": "D vs E is the whole answer: the null-prototype spelling that compiles buys the runtime fix by deleting the compile-time guarantee that a driver row added without a JSON-Schema projection fails to build. This is the same trade src/shared/value-domain.zod.ts recorded for #15315, now re-measured against THIS file's typing rather than inherited. The hypothesis's stated OUTCOME (no declared return type moves, patch level, no clause ②, no migration entry) is nevertheless achieved — by the hasOwnProperty guard instead."
},
"fence_reading": {
"adr_0087_migration_entry_needed": false,
"evidence": "node scripts/check-adr-0087-registration.mjs --base origin/main exit 0 — 'this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'. Nothing authorable is retired or renamed and no stored metadata key moves.",
"fenced_paths_touched": "0 of 3. git diff --name-only origin/main...HEAD is 3 paths; grep -c on packages/spec/src/migrations/ = 0, on packages/spec/src/ui/component.zod.ts = 0, on packages/spec/scripts/ = 0. LIT CONTROL: grep -c on packages/spec/src/data/driver/ = 2 (this diff's own paths), so the probe is discriminating.",
"consequence": "This card can land in parallel with #17439, #17334 and the packages/spec/scripts/** serial chain."
},
"clause_2": {
"declared_at_dispatch": "no",
"re_declared": "no — unchanged",
"evidence": "pnpm --filter @objectstack/spec check:api-surface exit 0: '@objectstack/spec public API surface + factory signatures unchanged ✓'. packages/spec/api-surface/data.json is NOT in the diff. node scripts/pm/check-clause2-carriers.mjs --pair 17457 exit 0: 'the clause-② declaration is readable in the fixed spelling and both carriers agree, and its diff carries no widening tell'.",
"needs_contract_review_label_applied": false,
"why_not": "No published signature moves. getDriverConfigJsonSchemaById stays (id: BuiltinDriverId) => Record[string, unknown]; both resolvers stay (driver: unknown) => BuiltinDriverId | undefined. The new lookupDriverId helper is module-private, not exported."
},
"changed": {
"files": 3,
"list": [
"packages/spec/src/data/driver/config-registry.zod.ts — one private lookupDriverId helper carrying the hasOwnProperty guard for both resolvers; a hasOwnProperty guard + named TypeError refusal in getDriverConfigJsonSchemaById; TSDoc on the accessor now states the totality",
"packages/spec/src/data/driver/config-registry.test.ts — one new describe, 8 cases over the off-vocabulary population",
".changeset/driver-config-registry-off-vocabulary-lookup-guard.md — patch, @objectstack/spec"
],
"refusal_shape": "THROW, not an optional return. The accessor's own docstring exists to say a caller enumerating drivers must not get a quiet
undefined; getDriverConfigSchema is already the optional alias-following door; and widening the return is exactly the published-signature change clause ② is about. TypeError rather than this module's usual Error (84 sites, 0 TypeError) because the two ids that ALREADY threw threw a TypeError — the class every existing caller can catch is unmoved and only the message improves. Flagged as an open question."},
"tests": "TARGETED: pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/data/driver/config-registry.test.ts — echoed TARGETED_VITEST_EXIT=0, 26 passed (26). FULL PACKAGE: verify-lock -c 'pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec test' — VERDICT command-exit 0 (parts joined with && so the verdict covers both), Test Files 469 passed (469), Tests 13220 passed (13220). TYPECHECK: pnpm --filter @objectstack/spec typecheck — echoed SPEC_TYPECHECK_EXIT=0 (tsc --noEmit + check:scripts-typecheck + check:test-typecheck all green; the last reports 54 files / 261 errors / 145 pinned signatures held, unchanged). ABLATION: direction predicted IN WRITING before the run (file recorded pre-run) as 4 RED / 22 GREEN with each control named; OBSERVED exactly '4 failed | 22 passed (26)', the same four. MOVED: 'throws for an id naming a callable Object.prototype member', 'names the offending id and the legal vocabulary in every refusal', 'resolves an off-vocabulary spelling to undefined, never to a truthy non-id', 'answers true for an off-vocabulary driver in driverHasLocalDefault, never undefined'. STAYED GREEN and why they MUST: (1) 'holds this population HONEST' is a claim about the TABLE not the guard — if it moved, every refusal assertion would be asserting against a legal id; (2) 'still throws for a plainly absent id, exactly as it always did' is the narrowing control — proto/nope/''/com.vendor.snowflake threw a TypeError BEFORE the guard too, so the fix must not invent a new failure for input that already failed; (3)+(4) the two 'still answers every canonical id ... unmoved' cases are the vocabulary edge, one per accessor family; (5) all 18 pre-existing cases in the file — every one iterates BUILTIN_DRIVER_IDS / DRIVER_ID_ALIASES / a hand-written canonical spelling, i.e. exactly the population that behaves, and their staying green IS the card's thesis measured. ABLATION MECHANICS: the pin imports './config-registry.zod' RELATIVELY, so the subject is source not the package exports — no rebuild owed; on-disk landing proved by grep -c on both guard anchors BEFORE (1 / 1) and AFTER (0 / 0) plus a changed git hash-object blob (bc36311d... -> 0f4b3773...); restore ran from a trap RESTORE_FN EXIT INT TERM on absolute paths via
git checkout HEAD -- ABS_PATHand was PROVED by the restored blob equalling the HEAD blob bc36311d30fec39747496acd3a0a0cda4f649d85 AND bygit diff HEADbeing empty. Nothing from the ablation remains in the tree. LINT: eslint --no-inline-config --format json over the 2 changed source files — 2 files, 0 errors, 0 warnings; the narrowing is a measurement because eslint.config.mjs never enables type-aware linting for ANY file ('no parserOptions.project, no typed @typescript-eslint rules', stated and positively controlled at eslint.config.mjs:328), so this diff cannot move the verdict on a file it does not touch. Repo-widepnpm lintis CI's run.","gates": {
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack — 81 commands for the 3-path change set (76 before the changeset existed; the delta was check-empty-changeset ×2, release-rehearsal-clone --self-test, check:objectui-changeset, check:pm-changeset-deadline-census). Derived AFTER
git fetch origin main. The derivation names the repo and commit on its first line and --repo objectstack-ai/objectstack was asserted and held.","ran": 81,
"green": 77,
"red": 0,
"not_measured": 4,
"not_measured_detail": [
"pnpm --filter @objectstack/lint run check:doc-formula-expressions — exit 3, 'PREREQUISITE NOT MET — the workspace package @objectstack/formula is not built'",
"pnpm check:dual-build-cjs-loads — exit 3, 'Run pnpm build first. This is NOT a pass: nothing was measured.' (79+ unbuilt packages listed)",
"pnpm check:lean-entry-closure — exit 3, 'This is NOT MEASURED. It is neither a pass nor a failure, and exits 3 so nothing reads it as either.'",
"pnpm check:type-check-debt — exit 3, its own re-measure prerequisite"
],
"not_measured_note": "All four want a whole-repo build this task deliberately did not run (only @objectstack/spec was built). ⛔ None of the four is red, and none is counted in the 77 green. Every exit code was captured before any pipe (cmd > log 2>&1; code=$?).",
"named_singles": {
"check:api-surface": "exit 0 — 'public API surface + factory signatures unchanged ✓'",
"check-adr-0087-registration --base origin/main": "exit 0 — 'this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'",
"check:authorable-surface": "exit 0",
"check:nul-bytes": "exit 0 — 8258 text files scanned, no raw ASCII control bytes; plus a manual grep -naP over the 3 changed files with a lit control (hasOwnProperty, 2 hits/file) and a dark control (fabricated term, 0)"
}
},
"ci": {
"read_at": "immediately after the draft PR was opened — NOT waited on; convergence is the PM's read",
"population_note": "two different populations, reported separately as asked",
"newest_run_per_check_NAME": {
"distinct_names": 31,
"completed_success": 8,
"completed_skipped": 3,
"in_progress": 20,
"completed_failure_or_other": 0
},
"combined_commit_status": {
"state": "success",
"contexts": 1,
"detail": "Vercel — success. This is the legacy commit-status API and sees ONE context; it is not a verdict on the 31 check-runs above."
}
},
"mcp_calls": "1 — one targeted mcp__github__search_issues, used only for the de-duplication read before filing the out-of-scope finding, because REST /search/* is refused by the egress proxy by design. Declared as a channel switch. Its LIT CONTROL was self-serving: the query returned #16903 itself and the family sibling #15315 (closed), so an empty result would have been distinguishable from a broken channel. Everything else — the card body, the triage comment, the claim comment, the issue creation, the PR creation, the PR body read-back, check-runs, combined status and labels — went through repo-scoped REST (probed first: GET /repos/objectstack-ai/objectstack returned 200) or plain git.",
"open_questions": [
{
"question": "getDriverConfigJsonSchemaById now throws a TypeError. This module throws
Errorat 84 sites andTypeErrorat 0. Is preserving the error CLASS for the two ids that already threw worth deviating from that 84:0 convention?","options": [
"A — keep TypeError (shipped). The two ids that already threw threw a TypeError, so no existing catch changes class; TypeError extends Error, so
catch (e) { e instanceof Error }is unaffected; and a wrong-argument condition is what TypeError means.","B — switch to
throw new Error('getDriverConfigJsonSchemaById: …'), matching the module's 84 existing sites, and accept that__proto__/nopechange error class."],
"recommendation": "A, because it is the strictly narrower change: under B the two ids that ALREADY failed would fail differently, which is the one thing a narrowing fix should not do. Cheap to flip if the seat prefers the convention — one line and one test assertion."
},
{
"question": "The claim says 'session id in body PROSE, ⛔ not a hand-written footer — both MCP and raw REST append their own', while the standing dev contract says a created PR body ends with a session-URL footer. Measured rather than guessed: raw REST appended
_Generated by [Claude Code](https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH)_to PR #17457 by itself, and the bare form to issue #17456. I wrote NO footer and put the session id in prose. Recording the conflict rather than silently picking a side.","options": [
"A — the claim wins for this card (what I did): no hand-written footer, session id in prose, platform footer is the attribution.",
"B — write the footer by hand and accept a duplicate."
],
"recommendation": "A. The read-back proves the platform footer landed exactly once and in the session-URL form, so B would only duplicate it."
}
],
"out_of_scope_findings": [
"filed as #17456: canonicalizeSqlType (packages/spec/src/data/type-compat.ts:222) returns the Object FUNCTION out of a CanonicalSqlType string union for 'constructor', and suggestDefaultValueToken (src/data/default-value-shape.ts:297) returns the Object function for 'constructor' and Object.prototype for 'proto' — two more live members of the #15315 / #16903 family, both published, both measured against the same built artifact. CARRIER: the domain:spec execution seat, the same seat that carries #16903 and #15315; filed unassigned and unlabelled per this repo's triage convention. De-duped first with the single MCP search above (lit control hit: #16903 and #15315 both returned; no existing card covers either function). The card also records the two family siblings that are SAFE and why — numericColumnFor has a real Set membership guard, currencyFractionDigits is safe only by an upper-casing accident — so the next seat does not fix four things where only two are holes.",
"noted, not filed: DRIVER_CONFIG_SCHEMAS is itself a published object literal, so a consumer indexing it DIRECTLY with an untrusted key still resolves a prototype member (measured: Object.getPrototypeOf(DRIVER_CONFIG_SCHEMAS) === Object.prototype is true). Closing it needs the null-prototype table this round measured as the worse trade (TS2741 stops firing), so filing it would file a request to do the thing measured as harmful. CARRIER: the guard's own comment in packages/spec/src/data/driver/config-registry.zod.ts, which the next editor of that file reads in place — the measurement and the rejected alternative are both recorded there.",
"noted, not filed: the two resolvers' .toLowerCase() / currencyFractionDigits' .toUpperCase() accidentally shield SOME prototype names (tostring, valueof, CONSTRUCTOR name nothing) while leaving the already-correctly-cased ones exposed. Not a separate defect — it is the mechanism behind the two above and is recorded in the lookupDriverId doc comment and in #17456. CARRIER: #17456."
],
"notes": {
"assignee": "os-bill, set by the PM's dispatch atom. Inherited, never written by me. No second Claim: comment was posted.",
"labels_on_pr": "documentation, size/m, tests, tooling, protocol:data — all applied by the repo's auto-labeler. No label was owed by me: clause ② is
no(so no needs:contract-review) and a changeset exists (so no skip-changeset). Read back after the PR was opened; nothing was stripped.","ready_flip": "NOT done. PR is draft. No enqueue, no auto-merge.",
"verify_lock": "One exit-99 (queue-timeout, 240s budget) on the first baseline build — recorded as NOT MEASURED, the slot 'issue-16903-spec' was kept, and the run was resumed under the same slot rather than re-queued from the back. Four subsequent locked runs acquired.",
"worktree": "/home/user/objectstack-issue-16903, removed after the PR was opened."
}
}
Generated by Claude Code
fix(spec): guard three data lookups against Object.prototype fall-thr…