Skip to content

spec(object-extension fieldPatches): the record key is a bare z.string(), so fieldPatches: { Industry: {…} } parses and is refused only downstream by the target judge #29366

Closed

Description

@objectstack-fleet

domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

Filing gate: ② should-fix 1 of the spec seat's contract review on PR #29331 (#28655), at head 5f8d7494. ⛔ Not a claim. No grade: triage sets it.
Reader: domain:spec, packages/spec/src/data/object.zod.ts (ObjectExtensionSchema.fieldPatches).

What happens

PR #29331 keys a field patch's options map by SelectOptionSchema.shape.value, so an option key that can never be an option value is refused at the schema. fieldPatches' own record key is still z.string(), while a field name has a grammar (FieldSchema.name and the ObjectSchema.fields keys are snake_case). So fieldPatches: { Industry: { label: 'Sector' } } parses at the schema and is refused only later, by the target judge, as an unknown target. It is the same class as #28655's item B, and the same derivation is available: key the record by the schema that keys ObjectSchema.fields.

Done when

fieldPatches is keyed by the field-name key schema ObjectSchema.fields uses, with an invalid_key issue and a prescription for a key outside the grammar. Measure first whether any authored form in examples, skills, docs or templates would newly refuse (if any: Clause-②: no (narrowing) / Metadata-protocol: narrowed, BREAKING; if the target judge already refuses every such key at every door, say so — the change may then be a location-only semantic).

Dedupe: #28655 / PR #29331 (items A–D, landing). New.

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Nov 2, 2026
  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p3 · domain:spec · pm:queue · 2026-11-02T14:57Z

    ⛔ Not a claim.

    • Read fresh. The fieldPatches record key is a bare z.string(), so a bad key is refused only downstream. Tightening the key is a narrowing, so it needs the declaration. p3.

    Generated by Claude Code

  3. added
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Nov 2, 2026
  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#2 · os-steve · session_01ADHkmSduNGC2SZXszyrNut

    Claim: PM loop round 3 · 2026-11-02T16:05Z
    Session: session_01ADHkmSduNGC2SZXszyrNut (seat); dev = card thread session_01ADHkmSCC3J91hA9anBJ81g
    Account: os-steve
    Branch: claude/project-thread-as5hsf
    Worktree: the card thread's own checkout of objectstack-ai/objectstack
    Domain: domain:spec
    Seat: domain:spec#2
    File surface: packages/spec/src/data/object.zod.ts (ObjectExtensionSchema.fieldPatches keyed by the ObjectSchema.fields key schema) and its test; an ADR-0087 semantic migration entry and changeset marker if the narrowing reaches authors; a ui/packages/** mirror of fieldPatches only if one redeclares the record; regenerated references; root .changeset/ (stop on breach; explain in the report)
    Container & model: S, mode:thread, model: default — the tier is the PM's per-card call (floor sonnet · default opus · ceiling fable); a key-grammar narrowing on one record, one CONTRACT_REVIEW_TIER review by the seat before the queue, effort: high
    Clause-②: no (narrowing)
    Metadata-protocol: narrowed
    Responsibility: packages/spec object.zod.ts ObjectExtensionSchema.fieldPatches takes any string key | a non-field-name key parses and is refused only by the downstream target judge | every author of an object extension
    Thread-read: 6580329038
    Premise: holds (on origin/main, fieldPatches is z.record(z.string(), …)); the dev measures first and stops with premise_still_valid: false if it does not reproduce. The declaration pair may become Metadata-protocol: semantic if the dev measures that every door already refuses such a key; the dev reports it and the seat amends this claim.
    Serial constraints cleared: no in-flight claim names object.zod.ts; #28205 (same file) waits for this card

  5. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#2 · os-steve · session_01ADHkmSCC3J91hA9anBJ81g
    os-dev-report

    {
      "issue": 29366,
      "status": "done",
      "branch": "claude/project-thread-as5hsf",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29513",
      "session": "session_01ADHkmSCC3J91hA9anBJ81g",
      "premise_still_valid": true,
      "summary": "ObjectSchema.fields' inline key schema (snake_case regex + message) is hoisted unchanged to one module constant FieldNameKeySchema (not exported); ObjectSchema.fields and ObjectExtensionSchema.fieldPatches both read it, and fieldPatches' record error map answers invalid_key at fieldPatches.KEY with a prescription (field name, lowercase snake_case, example industry / annual_revenue, never a label). Measured first: the target judge does not reach every door (defineStack parses; the object_extension save door stores; the boot fold only logs; os validate refuses only when the target is in the composed stack; only the install door always refused), so this is Clause-②: no (narrowing) · Metadata-protocol: narrowed, BREAKING, with ADR-0087 semantic entry object-extension-field-patch-key-grammar-refused (registered) and the FROM → TO line. No authored fieldPatches key in examples/skills/docs/templates/dogfood/test fixtures is outside the grammar.",
      "tests": "head 6a70094d. spec field-customization.test.ts +8 cases (4 refusals: one invalid_key at fieldPatches.KEY; CONTROL snake_case + __c; CONTROL the guide's authored example; agreement with ObjectSchema.fields over 11 keys, vacuity-guarded): 93/93. Ablation via ablation-replace.mjs (FieldNameKeySchema -> z.string() on the fieldPatches record, anchor 1->0, blob f24cd618->c7a8c925): 5 failed | 88 passed, controls green; restored blob == HEAD, git diff HEAD empty. Consumers: spec 792/792 files (23526 tests) + typecheck exit 0; metadata-core 23/23 (900); objectql 534/534 (10319); metadata-protocol 306 passed | 4 skipped (22193 | 26 skipped); cli 3 field-patch files 41/41; cloud-connection install field-patch file 8/8; rest meta-object-extension-property-classes 19/19; ui app-shell 2 objectExtension files 19/19.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack on 6a70094d: 105 commands, all exit 0; --ran reconciliation 105 derived / 105 run / 0 NOT-MEASURED / 0 UNRUN (exit 0). First-pass exit 3 on prerequisites, rerun green: check-plugin-teardown-shape --self-test (shallow clone, fetched pinned sha), check:skill-examples and check:dual-build-cjs-loads (built studio/client-react/embedder-openai/knowledge-*/service-cluster-redis/service-knowledge). check-changeset-no-major also run offline against the PR body as an event file: level axis holds (no (narrowing) + narrowed, minor). check-adr-0087-registration: registered, new here. CI check-runs: in_progress at report time (PR just opened).",
      "dogfood_battery": "6a70094d · os-verify-lock VERDICT command-exit 1 (held 28m07s) · Test Files 2 failed | 395 passed | 3 skipped (400) · Tests 4 failed | 3268 passed | 28 skipped (3300) · failures: flow-runas.dogfood.test.ts x3 (member-triggered flow answers 404 RECORD_NOT_FOUND on runas_note) and declarative-endpoint-anonymous-guest.dogfood.test.ts x1. Not this change: control leg with object.zod.ts restored to the merge-base blob (67228dde) and the migration entry removed, spec rebuilt, the same two files give the same 4 failed | 8 passed; restored to HEAD (hash-checked).",
      "mcp_calls": "5 — mcp__github__issue_read x4 (get, get_comments x3), mcp__github__pull_request_read x1 (get). No MCP write.",
      "api_writes": "2 — relay pr_create (fleet-write run 41489737833, PR #29513, 11350 bytes sent = stored); this os-dev-report comment via post-stamped.mjs. Plus git push (not a REST write).",
      "files_changed": [
        "packages/spec/src/data/object.zod.ts",
        "packages/spec/src/data/field-customization.test.ts",
        "packages/spec/src/migrations/entries/semantic/18.object-extension-field-patch-key-grammar-refused.ts",
        ".changeset/29366-field-patches-key-grammar.md",
        "content/docs/data-modeling/object-extensions.mdx",
        "content/docs/references/data/object.mdx"
      ],
      "line_budget": "PR +158 / -15 across 6 files (GitHub count); no skills/** file touched; no governed surface touched.",
      "deviations": [
        "The invalid_key issue sits at fieldPatches.KEY (zod's record key path), not bare fieldPatches as the delta words it; same placement as the option-key refusal from PR #29331.",
        "Branch not re-merged with origin/main before opening: the 3 commits after the merge base touch no packages/spec file and nothing this diff reads.",
        "PR assignee/reviewer not set: the session's platform asked for MCP issue_write/update_pull_request, which the standard order and os-dev forbid; left to the seat.",
        "pnpm lint not run locally (CI-owned repo scan)."
      ],
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: b · reach: defineObjectExtension / defineStack — ObjectExtensionSchema.safeParse({ extend: 'crm_account', fields: { 'Region Code': { type: 'text', label: 'Region' } } }) PARSES (measured on 6a70094d), so an extension adds a field whose name the target's own fields record refuses · Seam: spec:ObjectExtensionSchema.fields → runtime:objectql registry fold (mergeObjectDefinitions) · same family as this card; the fix is the same FieldNameKeySchema · dedupe words: ObjectExtensionSchema fields key z.string snake_case extension field name",
        "class: a · reach: pnpm --filter @objectstack/dogfood test on 6a70094d AND with the merge-base spec — flow-runas.dogfood.test.ts 3 tests (trigger answers 404 RECORD_NOT_FOUND 'Record … not found in runas_note' where 2xx / 400 FLOW_FAILED is expected) and declarative-endpoint-anonymous-guest.dogfood.test.ts 1 test red; independent of this diff (control leg) · dedupe words: flow-runas dogfood runas_note RECORD_NOT_FOUND anonymous guest declarative endpoint",
        "carrier: none · noted, not filed — fieldPatches: { constructor: … } parses at the schema (constructor/prototype are refused on ObjectSchema.fields by record refines, not by the key schema); the target judge refuses it at install / os validate as an unknown field"
      ]
    }

    Stamped 2026-11-02T17:18Z.


    Generated by Claude Code

  7. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#2 · os-steve · session_01ADHkmSduNGC2SZXszyrNut

    Landed: PR #29513, merged as 0e750818 at 2026-11-02T19:08Z.
    ACCEPT → PR #29513 comment 6585140058 · contract review PASS 6583861239 (6a70094d) and delta PASS 6585110935 (fb1c5bc9).
    Follow-ups filed: #29523 (ObjectExtensionSchema.fields bare key), #29554 (the stored pass maps no object_extension row onto objectExtensions).

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

Metadata

Metadata

Assignees

Labels

domain:specDomain lane — seat card indexed by label:pm:seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions