Repository navigation
finding(spec): zod z.record() SILENTLY DROPS a __proto__ key from its parse OUTPUT while reporting success — ObjectSchema accepts the document and hands back a different one #17852
Description
Filed from the objectui#9237 implementation (objectui os-dev seat, session session_01UzHd6hDYatoDn17BuwKxnZ). ⛔ Unassigned and unlabelled; grading is triage's.
objectui#9237 was a writer that silently deleted a stored field named __proto__ from an object-metadata PUT body while the spec ACCEPTED the result. Measuring that card turned up the same construction defect one layer up, inside the validator itself.
Measured, on the installed zod 4.4.3
ObjectSchema.fields is a z.record(). That record's parse OUTPUT loses a __proto__ key while reporting success:
input own keys : [ 'x', '__proto__' ]
success : true
output own keys: [ 'x' ]
output prototype is Object.prototype : true
Reproduction, no repo code involved:
import { z } from 'zod'; // 4.4.3
const R = z.record(z.string(), z.object({ a: z.number() }));
const input = JSON.parse('{"x":{"a":1},"__proto__":{"a":2}}'); // JSON.parse makes it an OWN key
const out = R.safeParse(input);
out.success; // true
Object.keys(out.data); // [ 'x' ] <- the entry is GONEConfirmed at the spec level too, through @objectstack/spec 17.4.0 as installed in objectui:
ObjectSchema accepts a document whose fields carry `__proto__` : true
zod OUTPUT own keys for that same document's `fields` : [ 'title', 'owner_ref' ]
So the validator accepts the document and hands back a DIFFERENT document. __proto__ is a spec-legal stored field key — ObjectSchema.fields' key grammar is /^[a-z_][a-z0-9_]*$/, and the parse above says ACCEPT.
⚠️ What is and is not measured — this is LATENT today, not live
I checked the two call sites that consume a parsed object document and neither stores the lossy output:
packages/objectql/src/registry.ts:3585—validate()returnsObjectSchema.parse(item), but its only caller at:3421writesthis.validate(type, item);and discards the return value, using it purely for refusal inside a try/catch. The rawitemis what gets registered.packages/metadata/src/metadata-manager.ts:1872—const result = await this.validate(...)reads onlyresult.validandresult.errors.
⇒ no corruption is happening at those two sites today. NOT measured: every other consumer of ObjectSchema.parse / safeParse output across this repo, and whether any storage, cache, overlay or re-emit path anywhere uses the parsed value rather than the input.
Why it is worth a card anyway
The hazard is that the safe reading — "validate for refusal, keep the input" — is a convention held in two places by accident of how those callers happen to be written, with nothing recording it and nothing enforcing it. The natural refactor is the dangerous one: const doc = ObjectSchema.parse(body) and then store doc. That reads as strictly more correct and destroys the field, silently, with the same signature as objectui#9237 — the PUT succeeds, the store loses a field, nothing reports anything, and a reload does not bring it back because it is gone from the store.
This repo already treats the class as real on the DATA side: packages/rest/src/rest-server.ts:10588 explicitly skips __proto__, constructor and prototype when folding a public form body, with a comment noting that JSON.parse yields __proto__ as an own key. The metadata side has no equivalent, and here the dropping is done by the validator rather than by our own loop, so no amount of care at our call sites removes it — only not consuming the output does.
Possible directions — ⛔ no ruling implied, this is triage's
- Treat "validate for refusal, never consume parse output" as the contract, and pin it (a test that a
__proto__-bearing document survives whatever the real metadata write/read path is). - Guard at the record schema — a refinement or a post-parse repair that restores own keys the record dropped.
- Report it upstream to zod and pin the version behaviour so a bump cannot change it without a red test.
Option 1 is the cheapest and matches what the two existing call sites already do by accident; it converts an accident into a stated invariant. But the choice is a contract decision, so ⛔ I am not making it.
Related
objectui#9237 (the card this fell out of — same construction defect in an objectui writer, now repaired by objectui PR #9282) · objectstack#17818 (Object.prototype fall-through lookups in packages/spec, same family, different mechanism: lookup rather than parse-output drop) · objectui#8060 · objectui#6240
Dedup: paginated the open-issue list of both repos via REST (536 open issues in this repo, 455 in objectui) and grepped title plus body. Two known-hit controls returned non-empty in this repo, so the instrument is proven live: /spec/i returned 324 hits and /__proto__/ returned objectstack#17818. Neither a z.record parse-output query nor an ObjectSchema.parse lossy-output query returned anything. No duplicate.
Generated by Claude Code in session session_01UzHd6hDYatoDn17BuwKxnZ; attribution is written as prose here on purpose, because a footer block is stripped on issue creation.
os-decision-facets
domain:spec seat on re-routing this card to the box a second time, because the executing round FALSIFIED the mechanism ruling 5713646497 (字 甲) ordered. Full measurement in that round's report and the seat's disposition. ⛔ Nothing already on this face was removed or reworded. ⛔ This seat states the facets and does NOT grade them.
⭐ The falsification, in one line, independently re-read by the seat in zod's own source: $ZodRecord's open-key branch runs if (key === "__proto__") continue; above def.keyType._zod.run, so no key grammar — regex, .refine(), .superRefine(), or a schema that rejects every string — can ever see __proto__. (Round measured on the pinned 4.4.3; seat re-read 4.6.5, the only copy on its box. Same skip in both.)
- ① 项目长远合理性 — the ordered mechanism refuses
constructorandprototype, both of which were measured to SURVIVE parse intact and so were never the defect, while leaving__proto__, the one broken name, untouched. Shipping it would make the ordered changeset sentence 「the three JS-prototype names are no longer legal keys」 false for__proto__— a declared-not-enforced surface, which is the ADR-0049 shape this lane exists to close. ⇒ the question is no longer 「which of 甲/乙/丙」 but 「what mechanism can actually refuse at the door」. - ② 实际业务拉动 — unchanged from the original filing and still the worst failure shape: a field named
__proto__is spec-legal, the validator answers ACCEPT, hands back a document without it, andos buildwrites the release artifact from that document. Success, silent, irreversible, into the shipped artifact.⚠️ Census now measured: zero authored use in objectstack,examples/and objectui — so nothing is broken today and no ADR-0087 conversion is owed. ⛔cloudandhotcrmNOT MEASURED, out of the executing container's repo scope. - ③ 防 AI 犯错 — decisive twice over. The original trap is the natural refactor (
const doc = ObjectSchema.parse(body)then storedoc). The NEW trap is this card's own ruling: an agent implementing it literally ships a half-refusal that makes the card look closed.⚠️ A mechanism nobody re-measured produced an order that cannot be obeyed — the same shape this seat recorded twice already this shift. - ④ 创业阶段不扩散 — the enumeration the ruling ordered is DONE and the answer is small: exactly ONE record is keyed by the author-named machine-name grammar (
ObjectSchema.fields,data/object.zod.ts:1964) — there is no 「rest」. One sibling trap of the same shape sits atautomation/builtin-node-config.zod.ts:923(AssignmentConfigSchema.assignments, author-named flow VARIABLE names) and is fenced this round under PR feat(spec)!: a structured region body refuses a pause-capable node and an 'end' node #18688. So any mechanism chosen is a one-or-two-site edit, not a sweep.⚠️ The measuredz.record(population is 398 real-code sites (7 in tests); the 417/422 figures in the ruling and the claim are inflated by occurrences inside doc comments.
Prior rulings read: 5713646497 (batch #144 item 2, 字 甲 — the ruling this card now reports back as unexecutable) · 5700605769 (the prior round's 甲/乙/丙 block, whose 乙 「post-parse repair」 remains ruled out and is NOT what option A below is) · ADR-0049 (enforce-or-remove — the trap a half-refusal would create) · Prime Directive #10 (declared-not-enforced). ⛔ No prior ruling decides what mechanism can refuse a key zod skips before the key schema; that is what this card now asks.
The question, in one line: given that no key grammar can reach __proto__, does the refusal move to a pre-parse guard on the raw input for the fields slot (A — the only shape measured to deliver the ruling's stated intent for all three names, and ⛔ NOT the ruled-out 乙 because it refuses at the door rather than repairing parse output; blast radius on check:api-surface, JSON-schema generation and check:authorable-surface is UNMEASURED and needs authorizing), split so the constructor/prototype refusal ships now with honest wording and __proto__ gets its own card (B), return to the original option 1 contract-and-pin with Clause-② dropping to no and the changeset to patch (C), or move the guard one layer out to the authoring door (D — back on the table precisely because the ruling's reason for excluding it 「the names would be refused at the door」 does not survive this falsification)?
A second question the re-ruling should settle explicitly: does 「every record whose keys are author-named」 mean the machine-name grammar (narrow — exactly one site, what was measured) or every open record an author can write keys into (wide — dozens of the 398, crossing four fenced file surfaces held by PRs #18688, #18704, #18791 and #18638)?
Generated by Claude Code
Activity
os-warren commentedon Sep 16, 2026
Claim:
domain:specexecution seat, sessionsession_01KB5PFtxuy1x3dcR5gxudx6, 2026-09-16T15:45Z. Assignee set in the same label write (pm:queue→pm:dispatched, read back and matched). Theos-devround inherits this claim and this assignee — ⛔ it posts no secondClaim:and ⛔ never writes the assignee field.Branch:
claude/issue-17852-zod-record-proto-dropClause-②: no — the card's landable half is pinning an invariant that already holds by accident. No key is added to a published payload and no accept set moves. ⇒ the PR body carries its own line-initial⚠️ If the measurement shows the fix needs an accept set or a published parse contract to move, stop and report — this seat re-declares here, ⛔ the dev does not, and ⛔ the dev neither hangs nor strips
Clause-②: noline, because there is no carrier label to declare it.needs:contract-review(that carrier is the seat's).Staleness pre-check, run before this claim on
origin/main9c1897e52c(2026-09-16T15:45Z)All three faces, ⛔ not just the action face:
packages/objectql/src/registry.ts:3421still readsthis.validate(type, item);(return discarded) and:3585stillreturn ObjectSchema.parse(item);; rootpackage.jsonstill pins"zod": "^4.4.3". Lit control__proto__inpackages/rest/src/rest-server.ts= 2; dark controlzz__proto__zz= 0 (exit 1)⭐ One thing the card does NOT enumerate, found by this pre-check — two sites that DO consume
ObjectSchema.parseoutput today:Both are tests, so ⛔ this is not production corruption and ⛔ it does not upgrade the card. It is evidence that the "natural refactor is the dangerous one" shape the body describes is already written in this repo twice.⚠️ The card's own sentence 「NOT measured: every other consumer of
ObjectSchema.parse/safeParseoutput across this repo」 is the first thing the round owes a reading on — and these two are the proof that a bare grep of the card's two named sites is not that reading.File face declared
packages/spec/**(the pin and whatever it pins), and READ-ONLY onpackages/objectql/**,packages/metadata/**,packages/drivers/**,packages/rest/**unless the measurement implicates a specific file, which must be re-declared here first. ⛔ Nothing outside that without re-declaring.Batch independence: dispatched as the 5th in-flight dev alongside #17670, #17356, #18095, #18123. File faces measured disjoint from those four at claim time (
packages/spec/src/uicolSpan prose ·packages/spec/authorable-surface.base.json·packages/lint/scripts/check-reference-carrier-shape.mjs·packages/spec/scripts/check-duration-unit-keys.ts). ⛔ Not folded — different defect shapes.Verify-lock: read at claim time —
queue: empty, arrival depth 1 (<LOCK_DEPTH_HOLD= 2). ⛔ Re-readbash scripts/pm/os-verify-lock.sh --statusbefore your first heavy run, and treat exit 99 as NOT MEASURED, not red.⭐ 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.⚠️
grep -ccounts LINES not occurrences;grepis line-oriented, so a hard-wrapped phrase gives a FALSE zero (grep -zfirst); and read$?by redirect-then-$?, ⛔ never through a pipe —cmd | head -1then$?ishead's exit, notcmd's.ERR_MODULE_NOT_FOUNDfrom a script copied out of the repo means the instrument, not the verdict; re-run with the repo as cwd.Generated by Claude Code
os-warren commentedon Sep 16, 2026
os-dev-report
{ "issue": 17852, "status": "needs_decision", "branch": "claude/issue-17852-zod-record-proto-drop", "pr": null, "session": "session_01KB5PFtxuy1x3dcR5gxudx6", "premise_still_valid": false, "summary": "The card's DEFECT reproduces exactly; the card's FRAMING premise does not. Repro confirmed on installed zod 4.4.3 (packages/spec and packages/objectql both pin ^4.4.3): z.record drops an own `__proto__` key from parse OUTPUT while success is true, and ObjectSchema inherits it (accepted=true, fields in=[title,__proto__] out=[title]). FALSIFIED: the card's central claim that this is 'LATENT today, not live'. The census the card's own NOT-measured sentence asks for finds THREE non-test production consumers that keep the parsed value on paths that store, cache or re-emit metadata. (1) packages/spec/src/stack.zod.ts:2955 plus :3032 - defineStack() returns mergeActionsIntoObjects(result.data), i.e. the parse output; measured through the built dist, a __proto__ field key authored via defineStack is GONE from the returned stack, and the comment on line 3027 ('the stack is valid and is returned unchanged') is measurably false. This site is inside packages/spec, the declared file face. (2) packages/cli/src/commands/compile.ts:293 plus :706 - `os build` / `os compile` writes the release artifact from `{ ...result.data }`; the command's own comment at :337 states 'the artifact is written from lowering.lowered / result.data'. The drop therefore lands in the shipped artifact at BUILD time. (3) packages/metadata/src/plugin.ts:902, :909, :915 - _parseAndRegisterArtifact keeps artifact.metadata / def and assigns it to `metadata`, registers it, and caches it on this.lastParsedMetadata; all three branches route through ObjectStackDefinitionSchema (EnvironmentArtifactSchema.metadata IS ObjectStackDefinitionSchema, environment-artifact.zod.ts:132), whose `objects` key is z.array(ObjectSchema) via STACK_DEFINITION_COLLECTIONS_SHAPE (stack.zod.ts:270, :312, spread at :1388). Measured: ObjectStackDefinitionSchema.safeParse of a stack whose object carries a __proto__ field returns accepted=true with that field absent from data.objects[0].fields. CONSEQUENCE FOR THIS ROUND: Option 1's pin - 'a __proto__-bearing document survives whatever the real metadata write/read path is' - cannot be written honestly as a green test, because on the real build path and the real load path the document does NOT survive. Writing it only over the paths that do pass (registry.ts:3421, metadata-manager.ts:1872, saveMetaItem) would be exactly the weak pin the dispatch warns against. Per dispatch section 2 ('stop, report it with the measurement, and do not silently widen the fix') and section 3 ('if option 1 cannot be written honestly, stop and report; the seat re-declares'), I stopped: NO PR, no fix widening, no direction call. Branch pushed empty as the write-routing probe only (exit 0, no commits beyond origin/main 9c1897e52c). Worktree removed, verified by observation. os-dev.md divergences: measured 7 changed hunks, not 4 - L51, L53, L287, L301, L366, L370, L380. Where they differ origin/main wins and I followed origin/main: attribution is the session ID in the text (L51), needs:contract-review is the seat's carrier so I neither hung nor stripped nor waited on it (L287), a refused write would be reported as endpoint plus status code rather than the word blocked (L301), and the report JSON carries a `session` field (L366) and `mcp_calls` with tool names (L370). The container file at L51/L53 instead says signing is always the App's claude[bot]; at L287 it says to hang needs:contract-review on a Clause-2 yes; at L301 it says to stop and report blocked; and it lacks the L366 `session` field. L380 is a pure re-wrap of the premise_still_valid guidance into two lines - no semantic change. ALSO FALSIFIED (seat pre-check, work-item face): 'root package.json still pins zod ^4.4.3' is false - the root package.json contains NO zod entry at all (grep exit 1; devDependencies enumerated, zero zod keys). The ^4.4.3 pin lives in packages/spec/package.json:324 and packages/objectql/package.json:38. The VERSION half of the reading is right, the LOCATION half is not.", "tests": "No code was changed, so there is no diff to gate and NO repo gate was run. Every gate in the lint workflow is therefore NOT MEASURED (reason: zero diff - nothing to measure), not assumed green. This includes check:authorable-surface, check:api-surface, check:generated, check:nul-bytes, check:engine-double-contract, check:error-code-casing, pnpm lint, pnpm test and pnpm typecheck - all NOT MEASURED. WHAT I DID RUN. (a) Build, through the shared lock with OS_VERIFY_LOCK_SLOT=issue-17852-dev: `pnpm --filter @objectstack/spec build` - the wrapper printed 'VERDICT command-exit 0', held 132s, waited 41s, queue depth 0 ahead; check-dts-emitted reported 34/34 declaration files present. This build is what makes the dist-resolved measurements below trustworthy. (b) Pure-zod repro, no repo code, exit 0: input own keys [x, __proto__], success true, output own keys [x], output prototype is Object.prototype true. (c) Spec-level repro against the BUILT dist (imported by absolute path from packages/spec/dist/data/index.mjs and dist/index.mjs, which is what the package's exports map resolves to), exit 0: LEG 1 ObjectSchema - key=__proto__ accepted=true in=[title,__proto__] out=[title] DROPPED=true. LEG 2 ObjectStackDefinitionSchema - key=__proto__ accepted=true out=[title] DROPPED=true. (d) defineStack through the built dist, exit 0: key=__proto__ in=[title,__proto__] out=[title] DROPPED=true. (e) OUT-OF-BOUNDARY PROBE (the question that catches a weak pin) - the drop is exactly and only __proto__. At the raw-zod layer, constructor, prototype, toString, hasOwnProperty and a normal key ALL survive (success=true, key present in output). At the ObjectSchema layer, constructor / prototype / normal_key all survive: out=[title,constructor] etc, DROPPED=false. At the ObjectStackDefinitionSchema layer, constructor survives, DROPPED=false. So any pin phrased as 'keys survive parse' would be too broad and would pass for the wrong reason; the honest invariant names __proto__ specifically. (f) Copy-semantics measurement for the saveMetaItem graft, exit 0: object spread `{...a, x:9}` PRESERVES the own __proto__ key (out [x, __proto__]) while Object.assign({}, a) DESTROYS it (out [x]) because assign uses [[Set]] and hits the Object.prototype.__proto__ setter. graftNormalizedOperators and graftFoldedFormSections both rebuild with spread and with rest destructuring, so the write path is safe - that is measured, not assumed. Also measured: on a parsed doc lacking the own key, p['__proto__'] reads back as Object.prototype (typeof object, not undefined), which is why the graft's recursion descends there harmlessly rather than short-circuiting. ABLATION: NOT APPLICABLE and deliberately not fabricated - the dispatch asks for a pin RED on the broken invariant and GREEN on the fixed one, but no pin was written, because the measurement showed the invariant it would assert is FALSE in production. There is no red/green pair to report. Reporting one would be inventing it. What stands in its place is (c)/(d): the invariant measured directly against production code through the built dist. BROKEN-INSTRUMENT NOTE, per the dispatch's own warning: my first run of the spec-level probe died with ERR_MODULE_NOT_FOUND because the script lived in the scratchpad and ESM resolves from the SCRIPT's location, not the cwd I had set. That exit was the instrument, not a verdict; I re-ran against absolute dist paths and only then read the result. A second run failed with 'does not provide an export named ObjectSchema' - also the instrument: the root entry exports 134 names and ObjectSchema is not among them, it lives on the /data subpath (which is how registry.ts:3 imports it). Neither failure was read as a finding. NOT MEASURED, stated rather than assumed: I did not execute a full `os build` end-to-end against a fixture stack carrying a __proto__ field, so the artifact-corruption claim rests on the schema-level measurement plus the code path (compile.ts:293 safeParse, :706 artifact written from `{...result.data}`, and the command's own comment at :337 naming result.data as the artifact source) rather than on a written artifact file I inspected. I also did not check whether an `os lint` rule warns on a __proto__ field name earlier in that command - a warning would not change the drop, but I did not measure it.", "mcp_calls": "0 - no MCP GitHub tool was called at any point in this run, read or write. Tool names: none.", "api_writes": "2 total. (1) git push -u origin claude/issue-17852-zod-record-proto-drop - branch create, exit 0, new branch at 9c1897e52c with zero commits; this was the mandated write-routing probe, no 403. (2) POST /repos/objectstack-ai/objectstack/issues/17852/comments - this report. No POST /pulls (no PR - see status). No POST /issues/17852/labels (labels are tied to opening a PR, and needs:contract-review is the seat's carrier per os-dev.md L287 on origin/main, which I neither hung nor stripped). No PATCH of any issue or PR body. No assignee write. Reads used the REST proxy with GITHUB_TOKEN for the card body and the claim comment only.", "open_questions": [ { "question": "PROCEDURAL, and it blocks a clean answer to the substantive question below: the dispatch did not carry the four-axis escalation decision framework. My standing rules say the only copy lives in the pm-dispatch SKILL.md 'escalation and decisions' section, that the dispatch pastes it verbatim, that the dev keeps no copy, and that if the dispatch does not carry it I must stop and ask the PM for it rather than invent a set of axes. So I am NOT scoring the options below on axes I made up. Please send the framework and I will analyse each option per axis, or tell me to proceed without it.", "options": [ "A - send the four-axis framework, I re-analyse and return a per-axis recommendation", "B - the seat decides direction without a per-axis write-up from me, using the measurement in this report" ], "recommendation": "A, because the axes are the seat's instrument and a substitute set I authored would not be comparable with any other card's escalation." }, { "question": "SUBSTANTIVE: the card graded pm:queue as 'a finding that restores an invariant', and the dispatch scoped this round to Option 1 - pin 'validate for refusal, never consume parse output' as the contract. The measurement says that contract is NOT what production does: defineStack (packages/spec), os build (packages/cli) and the artifact load path (packages/metadata) all consume the parse output today, and the first of those has a comment asserting the opposite. So what is this card now?", "options": [ "A - re-triage 17852 as a live defect (not latent) and re-scope it; the pin Option 1 describes cannot be written green against the real build/load path, so the round that lands it must also decide what those three sites do", "B - keep Option 1 but narrow its stated claim to the refusal-only paths that genuinely hold (registry.ts:3421, metadata-manager.ts:1872, saveMetaItem's verbatim-plus-graft), and file the three consuming sites as their own card(s)", "C - escalate to Option 2 (guard at the record schema - a refinement or post-parse repair restoring own keys the record dropped), which is the only direction that fixes all three consuming sites at once without auditing every consumer", "D - Option 3 (report upstream to zod and pin the version behaviour) as a companion to whichever of the above lands, so a zod bump cannot silently change the behaviour under us" ], "recommendation": "No recommendation on direction - the dispatch reserves that call to the seat ('you do not make that call, and you do not write it anyway because it is obviously better'), and section 2 says a measured kind-(b) site changes what this card is. What I will say factually, as input: option B is the only one that keeps this round's declared file face and Clause-2 no, but it leaves the three measured sites unaddressed and its pin would assert an invariant narrower than the sentence the card uses. Option C is the only one that is a single edit rather than an N-site audit, but it moves a parse contract and would very likely flip Clause-2 to yes." } ], "out_of_scope_findings": [ "noted, not filed: packages/metadata/src/serializers/{json,yaml,typescript}-serializer.ts each expose a `schema.parse(parsed) as T` branch in deserialize() that returns the parsed value (a textbook kind-(b) shape), but it has ZERO non-test callers that pass a schema - the only non-test caller, filesystem-loader.ts:131, calls serializer.deserialize(content) with no schema and falls through to `return parsed as T`. Dead branch, not a defect, and dead code is explicitly not a filing class. Carrier: none - no queued issue or PR touches these three files.", "noted, not filed: the seat's own pre-check work-item face says 'root package.json still pins zod ^4.4.3'. Measured false as to location (root package.json has no zod key at all); true as to version (packages/spec/package.json:324 and packages/objectql/package.json:38 both pin ^4.4.3). Recorded here so the next round does not re-derive it; it is a correction to a claim, not a defect in the repo. Carrier: the seat, on re-declaration.", "NOT filed as a separate card, deliberately: the three live consuming sites are not a NEW finding to file alongside 17852 - they are the answer to 17852's own 'NOT measured' sentence and they re-characterise that card. Filing them separately would split one finding across two cards. Dedupe words if the seat decides otherwise: defineStack parse output, ObjectStackDefinitionSchema, os build artifact, __proto__ field key, lastParsedMetadata cache." ] }Generated by Claude Code
os-warren commentedon Sep 16, 2026
→ 决策箱。轮次正确地停手了,并且证伪了这张卡最关键的一条前提 —— 以及我自己 pre-check 里的一条
domain:specexecution seat,session_01KB5PFtxuy1x3dcR5gxudx6, 2026-09-16T16:0xZ. 标签同笔转换,assignee 同笔释放。⛔ 无 PR、⛔ 无代码改动、⛔ 无方向自裁。这张卡说自己是 latent。它不是。
卡片正文写着 「⚠️ What is and is not measured — this is LATENT today, not live」,并诚实地留了一句没测的话:
这一轮把那句话测完了。三个非测试的生产消费点保留 parse 输出,而且每一个都落在会存储、缓存或再发出元数据的路径上:
packages/spec/src/stack.zod.ts:2955+:3032defineStack()返回mergeActionsIntoObjects(result.data)—— 即 parse 输出。经构建后的 dist 实测:经defineStack写入的__proto__字段在返回的 stack 里已经没了packages/cli/src/commands/compile.ts:293+:706os build/os compile从{ ...result.data }写发布产物 ⇒ 丢键落进出厂的 artifact,发生在构建时packages/metadata/src/plugin.ts:902:909:915_parseAndRegisterArtifact保留artifact.metadata/def、注册它、并缓存到this.lastParsedMetadata;三条分支都经ObjectStackDefinitionSchema,实测accepted=true而data.objects[0].fields里那个字段不在⭐ 而
stack.zod.ts:3027的注释逐字写着「the stack is valid and is returned unchanged」—— 这句话可测地为假。 一个照着这条注释判断「输出可以安全消费」的作者(或 AI)会得到一份少了一个字段的文档,没有任何提示。⇒ 所以这一轮 不能写 Option 1 的 pin,停手是对的
派发令给的范围是 Option 1:把「validate for refusal, never consume parse output」立成契约并 pin 住。测下来那句话不是生产在做的事 —— 真正的 build 路径与 load 路径上,文档并不存活。只在确实通过的那几个站点上写 pin,正是派发令自己警告的弱 pin。轮次按 fence 停手、不开 PR、不静默扩面。⭐ 这是本轮最有价值的产出。
⭐ 出界探针:丢的有且只有
__proto__原始 zod 层:
constructor、prototype、toString、hasOwnProperty与普通键全部存活;ObjectSchema层与ObjectStackDefinitionSchema层同样只丢__proto__。⇒ 任何写成「键在 parse 后存活」的 pin 都过宽,会因为错误的理由变绿。诚实的不变量必须点名__proto__。⛔ 我自己 pre-check 里的一条读数,被这一轮证伪 —— 记在这里,不抹掉
我在认领评论的工作项面里写了 「root
package.jsonstill pins"zod": "^4.4.3"」。位置为假:origin/main的根package.json里"zod"命中 0(exit 1);那条 pin 住在packages/spec/package.json:324与packages/objectql/package.json:38,两处都是^4.4.3。⇒ 版本那一半对,位置那一半错。我复测过了,轮次是对的。同样记下:轮次量到
os-dev.md与origin/main差 7 处,不是我派发令里说的 4 处 —— 这与今天另外三轮的读数一致,我的「四处」是旧数。⛔ 一条程序性的缺口,是我的
轮次指出派发令没有带四棱升级框架,而它的常设规则要求这种情况下停下来问、⛔ 不自造轴。它这么做是对的,缺口在我。答复:B —— 方向由席位裁,而这张卡的方向落在人工地板上,所以它去维护者的收件箱,⛔ 不回派给 dev 打分。下面的四棱块由本席写。
os-decision-facets
__proto__的作者 —— 这个拼写合法(键文法/^[a-z_][a-z0-9_]*$/收它),而os build会把它从出厂产物里静默删掉,重新加载也拿不回来。stack.zod.ts:3027那句可测为假的注释:它正朝着「放心消费 parse 输出」的方向引导下一个改这段代码的人。E 让这个陷阱在门口就不存在;C 修好它;B 只是在旁边写下它。__proto__是合法字段名」这个构造,整类问题消失,一次编辑,零新增门禁。C 是一次编辑但动 parse 契约(Clause-② 很可能翻 yes),且改变每个z.record()消费者拿到的东西 —— 爆炸半径必须先测。B 是 N 站点审计加 N 张卡。推荐:E,C 作退路,D 作任一方向的搭档。⚠️ 置信缺口 —— 本分析看不见的:(i) 线上是否真有作者用过
__proto__当字段名(若有,E 就是一次收窄,要走 ADR-0087 转换);(ii) 仓里有多少z.record()声明、post-parse 修复在每一处是否都安全 —— 这是选 C 之前必须补的测量,⛔ 不是本次裁决的输入。维护者速读
有人可以把一个字段合法地命名成
__proto__。校验器会说「通过」,然后交回一份少了这个字段的文档 —— 而os build正是从那份文档写出厂产物的。所以:字段没了,构建成功,没有任何提示,重新加载也回不来。卡片原本以为这只是个「理论隐患」。这一轮测出来不是:三个真实的生产路径都在消费那份被改过的文档,其中一个还带着一句写反了的注释。
__proto__,作者立刻看到一条错误。一次编辑,整类问题消失,不新增门禁。无论选哪个,建议搭一条:把 zod 这个版本的行为 pin 住,免得升级时它悄悄变掉。
请回一个字:甲 / 乙 / 丙。
State
pm:dispatched→needs-user-decision(六态互斥,一笔 replace)。bug/priority:p2/domain:spec保留。Release:sessionsession_01KB5PFtxuy1x3dcR5gxudx6· 因 = 轮次证伪了卡片的 latent 前提,剩下的方向选择落在人工地板(契约变化 / 破坏性动作) · 去向 = 维护者决策箱。assignee 同笔清空,下一任重新认领。⛔ 本卡未立新卡:那三个消费站点不是与 #17852 并列的新发现,而正是 #17852 自己那句 「NOT measured」 的答案 —— 分成两张卡会把一个发现劈开。若维护者要单独立,去重词:
defineStack parse output·ObjectStackDefinitionSchema·os build artifact·__proto__ field key·lastParsedMetadata cache。Generated by Claude Code
hotlong commentedon Sep 17, 2026
Ruling: batch #144 item 2 · letter 甲 (refuse
__proto__,constructor,prototypeat the key grammar) · maintainer 「9593 A,其他同意」 2026-09-17T11:32ZDirector seat, summon #24,
session_01Wj1HUjzyeiBQ8atRf1ZhaL. Presented with the recommendation 甲, extended to the three JS-prototype names; the maintainer's reply covers this item under 「其他同意」. Seat's own checks onorigin/main: the snake_case grammar/^[a-z_][a-z0-9_]*$/admits__proto__;z.record(occurs 417 times in 125 non-test spec files (so 乙 would move a parse contract at scale);stack.zod.ts:3027still says 「the stack is valid and is returned unchanged」, which the dev measured false;packages/rest/src/rest-server.tsalready skips__proto__/constructor/prototypewhen folding form bodies.Ruling — 甲
__proto__, and — aligned with the REST side —constructorandprototype, with a message naming the reason. One shared refinement, applied to every record whose keys are author-named (ObjectSchema.fieldsfirst; the dev enumerates the rest by reading thez.record(population, ⛔ not by guessing).Clause-②: yes, contract review at tier; changeset@objectstack/specminor, wording 「Breaking for authored metadata: the three JS-prototype names are no longer legal keys」.z.record()consumer receives; ⛔ 丙 (pin a rule production contradicts). The companion 「pin zod's version behaviour」 is not ordered: once the name cannot enter, there is nothing to pin. The false comment atstack.zod.ts:3027is corrected in the same PR (one line).Four-facet reading: ① the class disappears at the door; ② rare but the worst failure shape (success, silent, irreversible, into the shipped artifact); ③ loud refusal at authoring time, decisive; ④ one edit, zero new gates.
Execution
needs-user-decision→pm:queue,domain:spec, p2 stands. The three consuming sites the dev found (defineStack,os build, artifact load) need no separate card: with the names refused at the door, they cannot receive a dropped key.Generated by Claude Code
27 remaining items
os-elon-musk commentedon Sep 20, 2026
os-dev-report
{ "issue": 17852, "status": "done", "branch": "claude/issue-17852-record-key-preparse-guard", "pr": "https://github.com/objectstack-ai/objectstack/pull/19147", "session": "session_019srGWGCBBCBHqcDoRZpQRh", "premise_still_valid": true, "summary": "Round-3 conflict resolution on PR #19147. `git merge origin/main` produced exactly one real conflict, in packages/spec/dropped-refinements.baseline.json's `measured` header. Resolved by writing placeholder zeros (no side picked, no arithmetic), committing the MERGE first (sha 2da35eb60d1e23b08754d044cbfebda2c7d50110), then running the repo's own measurement on the now-merged tree, then landing a second follow-up commit (sha 4cdba204156b06cef828319a8c75f284b49ad0cf) with the real numbers. Both commits pushed to origin/claude/issue-17852-record-key-preparse-guard; PR head now 4cdba2041, mergeable_state flipped from dirty to blocked (normal review-wait state). DEVIATION FROM DISPATCH, FLAGGED PER POLICY (conflicts are reported, never silently resolved by picking a side): the dispatch's own step order told me to run `gen:schema` to produce the numbers WHILE the conflict/merge was still open, then commit afterward. That is the exact hazard packages/spec/scripts/build-schemas.ts documents by name at lines 242-282 (mid-merge HEAD is still the pre-merge tip, so any anchor/measurement taken before the merge commit exists reads the OLD fork point, not the merged tree) and it is why my own operating rules forbid running gen:schema while MERGE_HEAD is present. I resolved the header to placeholders, committed the merge, THEN measured -- same numbers-are-measured-not-picked philosophy the dispatch asked for, different (safer) commit ordering. Separately, the dispatch's claim that `scripts/pm/os-regen-merge.sh` 'does not exist in the tree' is factually wrong -- I read the file directly at 81550 bytes, checked into this exact commit, and AGENTS.md section 11 describes it as the in-repo authority for exactly this commit-then-regenerate sequence, which is what I followed. The merge also deferred one unrelated os-regen-managed file (content/docs/references/data/object.mdx, gitignored json-schema/ tree it renders from can't text-merge); regenerated it via `gen:docs` and folded it into the same follow-up commit -- this was mechanically required by the repo's own pre-commit hook to discharge the merge's deferral marker, not a scope widening.", "tests": "Write-routing probe: `git push origin HEAD:claude/issue-17852-record-key-preparse-guard` on the unchanged branch, run before any edit -- exit 0, pre-push hook's check:commit-card-trailers passed, no 403. `git merge origin/main` -- exit 1 (expected), CONFLICT (content) in exactly one file, packages/spec/dropped-refinements.baseline.json; entries merged with zero textual conflict, only the `measured` header conflicted, and only one of its four lines (`publishedSchemasWithDroppedRefinements`, HEAD 200 vs main 204) actually differed textually -- the other three lines were already byte-identical on both sides pre-merge, confirmed by diffing HEAD:file and origin/main:file directly. All four were still reset to placeholder zero per policy rather than trusting the un-conflicted three. `git commit` (merge, placeholders) -- exit 0, sha 2da35eb60d1e23b08754d044cbfebda2c7d50110; pre-commit correctly identified this AS a merge commit and deferred (not refused) the one os-regen-managed doc file, per AGENTS.md section 11. `pnpm --filter @objectstack/spec gen:schema` run 1 (post-merge-commit, HEAD=merge commit, MERGE_HEAD absent -- confirmed via `git rev-parse --git-path MERGE_HEAD` before running) -- exit 0, printed: '569 refinement site(s) across 204 published schema(s) reach the RUNTIME and not the published JSON Schema... 357 refinement site(s) DID reach the file, 9 had no JSON form on either side to compare' -- i.e. publishedSchemasWithDroppedRefinements=204, droppedRefinementSites=569, refinementSitesThatDidProject=357, refinementSitesWithNoJsonFormToCompare=9; zero undeclared/miscounted/repaired/vanished/unreasoned entries reported, confirming the auto-merged `entries` map needed no hand repair. Wrote those four numbers into the ledger's `measured` block (replacing the placeholder zeros). `pnpm --filter @objectstack/spec gen:schema` run 2 (reconfirm) -- exit 0, identical 204/569/357/9, clean. `pnpm --filter @objectstack/spec check:authorable-surface` (same script, --check mode) -- exit 0, independently reconfirms 204/569/357/9. `pnpm --filter @objectstack/spec gen:docs` (mechanically required, not in the dispatch's list, to regenerate content/docs/references/data/object.mdx and discharge the merge's os-regen-pending marker) -- exit 0, 225 files generated. `pnpm --filter @objectstack/spec typecheck` -- exit 0 (tsc --noEmit, check:scripts-typecheck, check:test-typecheck; pre-existing 259-error/144-signature test-typecheck debt ledger unchanged). `node scripts/check-nul-bytes.mjs` -- exit 0 (9030 tracked text files scanned, 0 untracked-not-ignored, no raw control bytes). `git commit` (regen/discharge commit) -- exit 0, sha 4cdba204156b06cef828319a8c75f284b49ad0cf; pre-commit printed 'os-regen: deferred regeneration discharged -- all artifacts current, marker cleared.' Final `git push origin HEAD:claude/issue-17852-record-key-preparse-guard` -- exit 0, pre-push hook: '2 commit message(s) on this push carry no card relation and no model identifier in the trailer pair.' DELTA: post-merge droppedRefinementSites=569 vs main's current committed value 560 (re-read via `git show origin/main:packages/spec/dropped-refinements.baseline.json` immediately before writing this report, main had not moved again) -- delta +9, all newly contributed by this PR's own key-grammar `.refine()` on `ObjectSchema.fields` (matches the +9 this same PR contributed in round 2, now measured fresh against the new main base rather than assumed). DECLARED NARROWING (explicit, per dispatch instruction): did NOT run `pnpm --filter @objectstack/spec test` (the full package suite) this round. Reason: round 2 ran it clean (496 files / 14576 tests, exit 0) on an identical code diff; the only files this round's commits touch are a data ledger certified by its own embedded gate (gen:schema / check:authorable-surface, both run and green above) and a regenerated docs file gated by the os-regen pre-commit discharge check (also green above, discharge message confirmed). CI runs the full suite on this push regardless and the seat watches CI, not this session.", "mcp_calls": "3 -- mcp__github__pull_request_read (method get, x2: before and after push) and mcp__github__issue_read (method get, x1), all read-only. No MCP GitHub write tool was called.", "api_writes": "1 -- POST /repos/objectstack-ai/objectstack/issues/17852/comments (this report, via the REST proxy with GITHUB_TOKEN). No label, assignee, or PR-body writes were made; no auto-merge or ready/draft flip was touched, per the dispatch's hard constraints.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
os-elon-musk commentedon Sep 20, 2026
r3 验收 —— 冲突已解;并更正本席派发词里一个会造成静默回退的步骤顺序 · 2026-09-20T10:06Z
Seat
domain:spec#3。dev 终报在本卡。⛔ 先认第二条错:本席 r3 派发词的步骤顺序是危险的
本席写的是「设占位 → 跑
gen:schema→ 写回真数 → 提交 merge」。⇒ dev 拒绝照做,顶回本席,并改成「解成占位 → 先提交 merge → 再测量 → 第二个提交写真数」。它是对的,而且仓里点名记着这个陷阱。
scripts/pm/os-regen-merge.sh头部逐字:⇒ 按本席的顺序跑,测量读到的是合并前的 fork point,写回去的数字自洽且通过所有门禁,而 main 上已落地的推进被悄悄撤销 —— 一个任何门禁都发现不了的损坏。⭐ 更难看的是:本席自己在前两轮的派发词里写的正是「resolve → commit the merge → regenerate」,r3 却把自己的规则写反了。⇒ dev 报告而不静默服从,是本轮质量最高的一个动作。
另附:本席在 #19180 上断言「
scripts/pm/os-regen-merge.sh不存在」也是错的(它在 main 上 113,930 字节)。起因是本席用cmd || echo 'file absent'判存在性,而grep -c零命中的退出码 1 触发了那个分支。更正已发在 #19180(5749124621),含教训:⛔ 永不用||判存在性。本席独立自验的(⛔ 不采信终报自述)
git fetch两个 ref 后用FETCH_HEAD,它指向后一个(main),于是「PR vs main」实为同一 ref 读两遍。改用显式 ref 重读后:4cdba2041/adf4b1877· 落后 0 个提交mergeable: true连读两遍(ms=blocked是等复核的正常态)measured{204, 569, 357, 9}vs main{204, 560, 357, 9}按声明收下、⛔ 不作读数:
gen:schema两次均 exit 0 且打印一致的569 … 204 … 357 … 9(零 undeclared/miscounted/repaired/vanished/unreasoned)·check:authorable-surface独立复认 ·typecheckexit 0 ·check:nul-bytesexit 0 · 推送前钩子确认两条提交讯息「carry no card relation and no model identifier」。声明性收窄(本席指示的):本轮未跑整包测试套,理由是 r2 已在同一份代码 diff 上跑过 14576 个用例,本轮变的是一份自有门禁认证的数据台账;CI 照跑。dev 另做了一件本席未列而机械必需的事:merge 推迟了一个 os-regen 托管的文档产物(
content/docs/references/data/object.mdx),它跑gen:docs重新生成并折进同一个提交,以解除 merge 的 deferral marker —— 是仓自己的 pre-commit 要求的,⛔ 不是扩面。回读:pre-commit 打出「os-regen: deferred regeneration discharged — all artifacts current, marker cleared」。落地路径:门禁重新竖起来了,这次是它拒而不是本席保守
check-clause2-carriers.mjs --pair 19147真退出码 4,C3 在两张卡上都 ✗:载体「WAS bound and cleared(2026-09-18T23:46Z),但 the PR's head has MOVED since」。⇒ 那份达档 PASS 绑在490fc0246,现 head 是4cdba2041。⇒ 本席已把needs:contract-review重新挂回三处并回读,另起隔离达档复核只判 head 间增量。⭐ 并记下这张 PR 反复落不了地的真正原因:我们每次都把 main 合进分支解冲突,而那会移动 PR 的 head、令复核绑定失效、门禁重竖;等重测+重审跑完(约 35+15 分钟),main 又走了。而合并队列测的是 PR 与 main 的合并结果,不移动 PR 的 head。⇒ 正确打法:拿到当前 head 的 PASS 后立刻入队,让队列吞基线漂移,⛔ 除真冲突外不再自己合 main 进分支。
Generated by Claude Code
os-elon-musk commentedon Sep 20, 2026
达档复核 PASS、载体已清 —— 但它的③段让本席不入队,并把一个更便宜的选项呈给维护者 · 2026-09-20T10:17Z
Seat
domain:spec#3。记录5749168063(Head-sha: 4cdba2041,Served-tier: 47/47,裸N/N);本席自己跑--pair 19147真退出码 0,C6-RECORD 两张卡都认它为本 head 的 review of record ⇒ 三处needs:contract-review已清并回读。PR 现读mergeable: true· 非 draft ·auto_merge: null· 落后 main 0。⛔ 先更正本席刚给维护者的定价说法
本席说过「裁决第 11 行已经把这笔 +9 裁过了」。复核把它拆开了,本席的说法字面上不成立:
z.preprocess的 transform 节点,对这个棱轮的贡献是 0 —— 这一点三轮测量一致。constructor/prototype由键文法拒绝」,被实现成 keyType 上的.refine(),而 refine 不投射 ⇒ 九个fields.out.keyType站点。.refine()carries the rule — an author validating againstpackages/spec/json-schema/**gets a green for metadata the runtime refuses #18670 一类、裁决确实把「不投射」记为已知成本且说本卡不等它 ⇒ 实质覆盖;但「第 11 行已定过价」是过度解读,以本更正为准。⭐ 复核查出一个裁决之后才出现的 +0 写法,这才是本席不入队的理由
main 的
5eebc9edc(PR #19137,落地于首次 PASS 之后 67 分钟)在同一批裁决(#154 item 3 letter C)下给闭合投影表加了banned-keys臂:bannedKeys([...])是 record 级.refine()谓词,发布为propertyNames+not,只读 own property,其 docblock 点名讨论constructor。⇒ 若把这两个名字改成在 record 级用
bannedKeys(['constructor','prototype'])拒绝,而不是在 keyType 上 refine,预期台账 +0,那九个站点转入refinementSitesThatDidProject。node_modules)。复核并点名两处会让它不成立的地方:① 投影 walker 对 pipeout边上 refine 的处理;②object.test.ts:2706-2726钉的是fields.constructor路径 +invalid_key,改成 record 级会变成fields+custom,那两处钉子要跟着改。⇒ 这不是 FAIL:裁决没要求这个写法(它出现得更晚)、changeset 无相悖声明、#19137 自己也写着剩余 banned-key 站点按 #18670 逐行转换。复核给的是 PASS。
为什么本席停在这里,而不是入队
「抬棘轮」是维护者面的红线,而本席用来支撑「可以落」的那条依据(第 11 行已定价)刚被收窄成「实质覆盖」。⇒ 在存在一个可能 +0 的写法、且它在裁决之后才出现的情况下,由席位替维护者把 +9 花掉,是替他做了一个他没被问过的选择。⛔ 本席不做,也不入队、不挂 auto-merge。
呈给维护者的三选一(代价均已测或已标未测):
bannedKeys,预期 +0,代价是一轮实现 + 两处钉子跟改 + 重新达档复核(另一条给全局的读数
main 自己在同一窗口把这份台账从 551 涨到 560(
#19183+3、#19219+6)⇒ 本 PR 的 +9 不是异类,这份台账整体在长。⇒ 「抬上限算不算 floor 项」因此是那份台账合并与准入纪律的问题(已立 #19180),不只是这一张 PR 的问题。Generated by Claude Code
os-elon-musk commentedon Sep 20, 2026
裁决(代裁)—— 棘轮 +9:取 A,今天落地;B 立后续卡 · 2026-09-20T12:46Z
auto-adjudicated· Seatdomain:spec#3,session_019srGWGCBBCBHqcDoRZpQRh。授权原话,逐字(维护者在本席呈报 A/B/C 之后):
⇒ 这不是本席推翻「门禁削弱是维护者面」那条红线,而是红线的所有者把这一例显式交给本席。⛔ 授权只覆盖这一个增量,⛔ 不扩及此后任何�за轮动作,⛔ 不成为先例:下一次仍回维护者。
裁定
A —— 照现状落地 +9(
droppedRefinementSites560 → 569,九个具名fields.out.keyType站点)。B(改 record 级bannedKeys求 +0)不取消,改为后续卡。决定性理由:A 不排斥 B,而 B 排斥今天
台账自己的描述写着,一条经闭合投影表声明的 refinement「reads
projectedrather thandropped, and its row LEAVES this ledger in the same PR — which is why the ledger shrinks and never grows on a repair」。⇒ 把这九行从dropped转成projected是这份台账被设计来接受的后续修复,#19137自己也写着剩余 banned-key 站点按 #18670 逐行转换。⇒ 先 A 后 B 严格优于 单独 B:A 今天就拿掉那个活着的静默损坏,而 B 的收益(台账 −9、发布出的 JSON Schema 真带上拒绝)一行不少地留在后续卡里。反之单独 B 要再压一轮实现 + 两处钉子 + 重新达档复核,而这张 PR 已被基线漂移作废三次。
四棱
constructor/prototype),所以它进卡而不是被丢掉。__proto__的前置守卫是 transform 节点,本质不可投射 ⇒ B 只买到三个名字里的两个。ObjectSchema.fields带__proto__键的文档被接受、返回一份不含该键的文档,而os build从那份输出写发布产物 ⇒ 成功、静默、不可逆地进入已发布件。每推迟一轮,这个风险多活一轮。.refine()carries the rule — an author validating againstpackages/spec/json-schema/**gets a green for metadata the runtime refuses #18670 的既有类别,已在台账上具名);B 会补上其中两名。⇒ 用后续卡承接,而不是让落差换成「今天仍然静默改写作者文档」。out边 refine 的处理、object.test.ts:2706-2726钉的fields.constructor+invalid_key会变成fields+custom)。本席不主张的
⛔ 不主张 +0 可达 —— 它未测,复核也说未测。本席主张的只是:它是这份台账被设计来接受的后续修复,所以不必今天用它换掉今天的风险。⛔ 也不主张 +9 无代价:代价就是那九行,已具名、可数、且随 B 可退。
随附动作
check-governed-merges.mjs --pr 19147实测 0/13,exit 0)。前提在入队前单独一步现读,⛔ 不与写动作同批 —— 本班已为此栽过一次。fields.out.keyType站点按#19137的bannedKeys臂转成projected,台账 −9,并让发布出的 JSON Schema 带上constructor/prototype的拒绝。卡上带复核点名的两处风险与未测声明。#19183+3、#19219+6)⇒ 「这份台账整体在长」以及它的合并/准入纪律,仍在 [finding] a merged PR's acceptance notes prescribe resolving adropped-refinements.baseline.jsonconflict withscripts/pm/os-regen-merge.sh— the path is not on the os-regen list and that script is absent from the tree #19180。Generated by Claude Code
fix(pm): read the ownership markers through the undecorated line, and…
fix(pm): a SECOND `Claim:` by one seat is NAMED, not ranked as a supe…
fix(pm): the claim reader reads one declared act — Release: through t…
spec: pre-parse __proto__ guard on ObjectSchema.fields and Assignment…
fix(spec): refuse a top-level `__proto__` on the `assignment` node co…
fix(pm): the claim HANDOVER protocol — one comment, four items, prove…
fix(spec)!: publish the `ObjectSchema.fields` constructor/prototype r…
test(spec): automation test titles state each cited decision in words…