Skip to content

feat(spec): ObjectSchema.attachedOnRead declares per-caller read attachments, and the shared validator judges record.BLOCK.LEAF against them - #22425

Merged
objectstack-fleet[bot] merged 14 commits into
mainfrom
claude/issue-22386-attached-on-read
Oct 9, 2026
Merged

objectstack-fleet[bot] merged 14 commits into
mainfrom
claude/issue-22386-attached-on-read

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #22386

Clause-②: yes (widening: a new optional ObjectSchema key)

The spec half of #22211's ruling A (6070963704), built under the claim 6073245703 as amended by the seat order 6074312931. #22387 follows: the plugin-approvals declaration of viewer on sys_approval_request and its conformance test. That card is blocked on this PR, and it is the one that finishes #22211; this PR leaves #22211 open, and its 8 shipped record.viewer.* predicates stay refused until #22387 declares the block.

What changes

  • ObjectSchema.attachedOnRead (packages/spec/src/data/object.zod.ts, beside fields). An optional map from block name to that block's leaves, each naming its value type: attachedOnRead: { viewer: { can_act: 'boolean', can_override: 'boolean', is_submitter: 'boolean' } }. These are the blocks a service attaches to each row it serves, computed per caller and never stored.
    • Both key levels take the field-name grammar. A leaf type is one of number | text | boolean | date, taken from FieldType (the four Field.returnType value types).
    • Refused at parse, each located at the offending key: a leaf type outside the four, a leaf that is a nested block or a field definition, a block or leaf name outside the grammar, a block that names no leaf, and a block that repeats a declared field name. The last two live in the object's existing superRefine, so no new check node reaches the JSON Schema projection.
    • Not a field. No driver, form, list view, export, write path or translation bundle reads it. git grep -c attachedOnRead at the head finds it only in packages/spec (the schema, its pins, the ledger row, the generated surface), packages/lint/src/validate-expressions*.ts, packages/formula/src/{validate,expression-refusal}*.ts, packages/metadata-core/src/object-schema-fls-references.ts (classified keep), the generated reference docs and the changesets.
  • Reader 1, the shared validator.
    • @objectstack/lint buildFieldIndex adds each declared block name to the field-existence set, and both call sites that pass the field index hand the shared validator the block's leaves.
    • @objectstack/formula checkFieldExistence judges the SECOND segment of record.BLOCK.LEAF (and previous.) against the declared leaves, through the new optional ExprSchemaHint.attachedOnRead, under the EXISTING unknown-field code. No new rule id, no new refusal code, nothing keyed on viewer.
    • The refusal: field is the dotted path as written, suggestion the nearest declared leaf, and the new optional block + leaves params carry the declared leaves, present together exactly when the message names them: unknown field `viewer.can_actt` on `approval` (the read attachment `viewer` declares `can_act`, `is_submitter`) — did you mean `viewer.can_act`?
    • The head scan RECORD_REF_RE is untouched (the second segment is read by a separate sticky regex), and the judgement runs only for a declared block, so an object without the key takes exactly today's path.
  • The leaf types' reader is plugin-approvals: sys_approval_request declares its per-caller viewer block under attachedOnRead, with a conformance test against attachViewers (#22211 ruling A, plugin-approvals half) #22387's conformance test, which pins the keys attachViewers emits and the runtime type of each value against the declaration (seat order 6074312931, recorded on plugin-approvals: sys_approval_request declares its per-caller viewer block under attachedOnRead, with a conformance test against attachViewers (#22211 ruling A, plugin-approvals half) #22387 as 6074324160). The liveness row says so.
  • Closed-world pins the new key moves (seat order 6074312931, Q1): the ADR-0106 D1 FLS position attachedOnRead: keep; the object form ledger's root omit row (the ruling says no form reads it); the composeStacks collection lists in the two pins, plus one row pinning that two stacks declaring different attachedOnRead for one object are refused under objectConflict: 'merge'. No stack.zod.ts change: the composition derives the set from the shape walk.
  • Ledger and generated artifacts. liveness/object.json grades the key live on an authoring consumer (the action.execution precedent), with producer, evidenceScope and verifiedAt. Generated by the repo's generators only: authorable-surface/data.json (build), liveness/state-counts/object.md (gen:liveness-counts), content/docs/references/** (gen:docs).
  • Changesets. @objectstack/spec minor (carries the Clause-② line); @objectstack/formula minor (additive public members); @objectstack/lint and @objectstack/metadata-core patch.

Merge round at a5104d854

After the contract review at e19c1e349 (PASS 6077844912; Test Core (6/6) was red only on #22415's timing-drift grade, which main reverted in #22435), origin/main was merged twice through os-regen-merge.sh, never rebased, with no conflict:

Measured at a5104d854, after pnpm install --frozen-lockfile and a full build (73 / 73):

  • lint 130 / 130 files, 5909 passed;
  • formula 45 / 45, 1278;
  • spec 630 / 630, 18791 + 1 todo;
  • gates: 111 derived, 111 run, 0 NOT-MEASURED, all exit 0.

Merge round at e19c1e349

After the contract review PASS 6076679507 at a0545b853, origin/main 05c7c3fa3 was merged through os-regen-merge.sh, never rebased.

Measured at a0545b853

The patch round after the contract review 6075426209 (FAIL on the sixth closed-world pin) merged main at 3054516ef cleanly through os-regen-merge.sh. All runs went through os-verify-lock (each VERDICT command-exit 0; shared-box seconds), after the metadata-protocol, lint and metadata-core closures were built:

tier result
@objectstack/metadata-protocol 223 / 226 files (3 env-gated live-DB skips), 28325 passed, 19 skipped. protocol.meta-types-degenerate-derivation.test.ts passes.
@objectstack/spec unit (--project local) 628 / 628 files, 18769 passed, 1 todo
@objectstack/lint 129 / 129 files, 5886 passed
@objectstack/formula 45 / 45 files, 1277 passed
@objectstack/metadata-core 17 / 17 files, 421 passed

Typecheck exits 0 for metadata-protocol and spec at a0545b853. lint, formula and metadata-core exited 0 at 4ba858882, and their sources are unchanged since.

Gates. dispatch-gates --commands at a0545b853 derived 111 commands: the previous 110 plus check:durability-log-level. All 111 exited 0 on the first pass, after a full turbo run build. --ran reconciles 111 derived, 111 run, 0 NOT-MEASURED, 0 UNRUN.

The closed-world inventory of ObjectSchema's top-level keys, searched before the push. Every list or count found is visited:

Derived sets needed no edit: the composeStacks shape walk, ObjectSchema.create()'s known keys, and the object form, where the key is an omit row.

Ablation: the misspelt-leaf pin bites. node scripts/ablation-replace.mjs on packages/formula/src/validate.ts, anchor if (leaves.includes(leaf)) return; replaced by return; plus a marker:

  • the anchor went x1 to x0, and the blob f7e24675 to ac2f0590;
  • validate-attached-on-read.test.ts: 3 failed, 6 passed. The misspelt-leaf, no-near-leaf and previous-root pins went red; the accept and control pins stayed green, which is the expected direction;
  • restore: blob == HEAD f7e24675, git diff HEAD empty, marker count 0; the restored run passed 9/9.

The ablation ran at 37711ce70. The file's blob at 4ba858882 is the same f7e24675. Formula's tests import ./validate from source, so the ablation has no dist leg.

Acceptance notes


Generated by Claude Code

claude added 7 commits October 9, 2026 02:59
…; pins + ledger row

Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn
Co-authored-by: Claude <noreply@anthropic.com>
…ed-world pins; type the unknown-field leaf params

Seat order 6074312931 widens the surface by the five measured files:
the ADR-0106 FLS position row, the object form ledger's root omit row,
the two composeStacks collection-list pins, and the unknown-field
block/leaves params with their codes pin. Formula changeset to minor;
the liveness note names the declaring package's conformance test as
the leaf types' reader.

Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/formula, @objectstack/lint, @objectstack/metadata-core, @objectstack/spec, touching 18 documentable anchor(s). ⚠️ 3 changed file(s) yielded no anchor (packages/spec/authorable-surface/data.json, packages/spec/liveness/object.json, packages/spec/liveness/state-counts/object.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/approvals.mdx (via can_act (literal, a string literal in AttachedOnReadSchema; a string literal in a comment on a changed line))
  • content/docs/concepts/metadata-driven.mdx (via ObjectSchemaBase (symbol, a top-level const object))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v16.mdx (via can_act (literal, a string literal in AttachedOnReadSchema; a string literal in a comment on a changed line))
  • content/docs/releases/v17/17-1.mdx (via can_act (literal, a string literal in AttachedOnReadSchema; a string literal in a comment on a changed line))
  • content/docs/releases/v17/17-7.mdx (via can_act (literal, a string literal in AttachedOnReadSchema; a string literal in a comment on a changed line))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/spec/authorable-surface/data.json, packages/spec/liveness/object.json, packages/spec/liveness/state-counts/object.md) — pages documenting those are invisible to this run
  • 1 anchor(s) matched too much of the corpus to be a work list: objectName (symbol, 38 pages)
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 140 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 2b61f2d9d6f8f7cc7f6b9f5f9772b8bc8fb0ea5d → packageMentionDocs.

Which tree this was computed on

This run read content/docs from ffbbac7facbdd55f961e89d7f9811a1b5d8459bc — the merge of head a5104d85433bdf7511d561694ebb0910750c488d into base 2b61f2d9d6f8f7cc7f6b9f5f9772b8bc8fb0ea5d, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ffbbac7facbdd55f961e89d7f9811a1b5d8459bc && git checkout ffbbac7facbdd55f961e89d7f9811a1b5d8459bc
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2b61f2d9d6f8f7cc7f6b9f5f9772b8bc8fb0ea5d a5104d85433bdf7511d561694ebb0910750c488d && git checkout -B drift-repro 2b61f2d9d6f8f7cc7f6b9f5f9772b8bc8fb0ea5d && git merge --no-ff a5104d85433bdf7511d561694ebb0910750c488d

node scripts/docs-audit/affected-docs.mjs --json 2b61f2d9d6f8f7cc7f6b9f5f9772b8bc8fb0ea5d

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 2b61f2d9d6f8f7cc7f6b9f5f9772b8bc8fb0ea5d → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4ba858882c1b907c52dd10232d139d5d313e30b7
Local-runs: none

Reviewer seat session_01DhTqaEHqPVSVnAkjG3jywn, reading at 2026-10-09T06:11Z. Inputs: card #22386 (body and every comment; the ruling 6070963704 on #22211 through the card's references), PR #22425 (body, the 21-file list, the net diff from merge base abd254508 to the head), and the head's check-runs. Read-only: the head was fetched into a private ref; nothing was built, run or re-run. The one log read (shard 6/6) came through the GitHub read API.

One line: FAIL on this head. The required Test Core gate is red (shard 6/6) and the red is this diff's — a closed-world pin in packages/metadata-protocol was not visited. The contract itself (①, ②) is right; the remedy is one line plus a claim widening, at the end of ③.

① Derived judgments

Accept-set changes in @objectstack/spec:

  • ObjectSchemaBase (a strictObject) admits one new optional key, attachedOnRead: a z.record of z.records, both key levels under the field-name grammar, each leaf one of FieldType.extract([number, text, boolean, date]). Right — matches ruling A (a strict record of the blocks a service attaches per caller; each block its leaf keys and their types; optional; not a field). Absence materialises no key (pinned); ObjectSchema.create() takes it through ObjectSchemaBase.shape (pinned).
  • Parse refusals: a leaf type outside the four; a nested block or a field definition as a leaf; a key outside the grammar at either level; a block with no leaf; a block that repeats an authored field name — the last two in the object's existing superRefine, located at ['attachedOnRead', block]. Right; no new check node, so the JSON-schema projection changes only by the property. One edge, not a defect: the collision set is the AUTHORED field map. A block named like an injected column (id, created_at, owner_id and the rest of resolveInjectedSystemColumns) parses clean and then shadows that column's second segment in the validator. Confined to the validator, self-inflicted, no silent data effect — F2 in ③.
  • ObjectExtensionSchema hand-lists its keys and does not gain the key: an extension cannot declare a block. Right — the declaration lives in code beside the attaching service, which is not a surface the ruling names.
  • ObjectStackSchema admits it under objects[]; composeStacks under objectConflict: 'merge' derives the key into its not-merged collection set from the shape walk (two pins updated, one it.each row added; stack.zod.ts untouched). Right (seat order Q2).
  • Generated artifacts: authorable-surface/data.json gains data/Object:attachedOnRead; three reference pages gain the .describe() row; liveness state counts 52 → 53. Generators only; Type Check · source gates (check:generated --reconcile-only, check:authorable-surface, check:docs) is green on the head. Right.
  • Liveness: liveness/object.json grades the key live on in-repo evidence naming attachedOnReadOf, buildFieldIndex and checkAttachedLeaf, producer runStackExpressionPasses; Spec property liveness is green. Right. The leaf TYPES have no reader in this card; the row says so in words and names the ruling's reader (2), plugin-approvals: sys_approval_request declares its per-caller viewer block under attachedOnRead, with a conformance test against attachViewers (#22211 ruling A, plugin-approvals half) #22387's conformance test — Q3 (iii) in ③.
  • No spec export is added or removed (AttachedOnReadSchema and refuseAttachedBlockConflicts are module-private), so no api-surface/ shard moves; Type Check · consumer gates (check:api-surface) is green. Right.

Public-surface changes in the three other packages:

  • @objectstack/formula: ExprSchemaHint.attachedOnRead? (block name → leaf keys) and ExpressionRefusalParams['unknown-field'] gains optional block + leaves, present together exactly when the message carries the leaves clause (pinned in expression-refusal-codes.test.ts). In checkFieldExistence, a known head that is an own key of the hint's blocks has its next member judged by SECOND_SEGMENT_RE (sticky at the head's end; a member followed by ( is excluded) and refused under the EXISTING unknown-field code, field the dotted path, suggestion the nearest declared leaf, dotted; the previous. root is judged the same way; one leaf is reported once. The reordered known/seen test is equivalent for unknown heads; RECORD_REF_RE is untouched; an object without the key, or with an empty one, takes today's path (pinned). Unjudged by design: index access, a method call, a third segment — each a missed catch, never a false refusal (pinned). Right: no new rule, no new code, nothing keyed on viewer.
  • @objectstack/lint: buildFieldIndex appends the declared block names after the injected columns, so record.BLOCK resolves; buildAttachedOnReadIndex threads block → leaves into BOTH call sites that pass the field index (the predicate check closure and the field-formula judge); the validate-expressions / validate-security-posture 也有同形的 spec 不声明键的 ?? 别名读法(#5009 建议 3 的核对结果) #5017 meta-guard tables obj.attachedOnRead and attachedOnReadIndex. The rule's accept-set widens only through the new key; no existing stack changes verdict. Right.
  • @objectstack/metadata-core: OBJECT_REFERENCE_POSITIONS.attachedOnRead: keep (the ADR-0106 D1 closed-world row); served definitions unchanged. Right.
  • Form ledger: one ROOT_PATH omit row for object, with the ruling as its reason. Right.

Reach of reader 1, measured at the head — right for the card's surface, and short of the text's claim in two places: the field-existence set is ExprSchemaHint.fields, built per caller. The diff covers lint's buildFieldIndex, the surface the card names. Two other builders exist and do not read the key: packages/mcp/src/mcp-http-tools.ts (the expression-validation tool builds fields from describeObject().fields[].name) and packages/services/service-automation/src/plugin.ts through setObjectSchemaResolver (flow conditions at registration, from registry.getObject(name).fields; checkFieldExistence runs in every scope). Reach-less today, since no object declares a block; the moment #22387 declares viewer on sys_approval_request, a record.viewer.can_act predicate validated through either door is still refused as an unknown field. F1 in ③.

The gate verdict this record turns on. Test Core (6/6) is failure on the head: @objectstack/metadata-protocol, src/protocol.meta-types-degenerate-derivation.test.ts, the case "control: object still serves 44 top-level properties" — expected 45 to be 44 at :326, the CARD_PROPERTY_COUNTS pin at :310–:313 (object: 44). The served object JSON schema gained the new key, so the positive-control count moved 44 → 45. This is a closed-world pin of exactly the class the seat order's Q1 widened the claim for, and the file's own docblock (:300–:309) records the precedent: "object moved 43 → 44 the same way when it gained imageField", "page moved 24 → 25 the same way when it gained print". The same shard is green on main at the PR's base 27a8b33 (the scheduled run), so the red is this diff's, not repo-wide. The round-1 report listed metadata-protocol as NOT MEASURED, named two other files in it and read them as unmoved; round 2 ran spec, lint, formula and metadata-core in full and metadata-protocol not at all, and the 110 derived gates did not reach this pin. Judged: the change is incomplete by one closed-world pin, and the diff must visit it.

② Semver level

Packages whose published files move: @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/metadata-core — all private: false, all at 17.7.0. Two changesets cover exactly those four; no skip-changeset; Check Changeset is green.

Clause-②: yes (widening: a new optional ObjectSchema key) — identical in the PR body (its third line) and in the spec changeset; (widening) is the correct arm; no major level anywhere.

③ Boundary flags

Round-1 open_questions (report 6074289657), each answered by the seat order 6074312931 and re-judged here:

  • Q1, widen by five files → A. The diff shows exactly those five and nothing else widened. Agreed, and incomplete: the class "closed-world pin that a new top-level ObjectSchema key moves" has a sixth member, packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts (judged in ①). The claim 6073245703, as amended, must widen by that file.
  • Q2, FLS keep and the composeStacks merge refusal → A. Agreed: neither reads a block's meaning.
  • Q3, four design choices → A. Agreed on (i) the two superRefine refusals, (ii) the four-type leaf vocabulary (a wider leaf type is a later widening with its own pull) and (iv) formula minor. On (iii): the leaf types are declared without a reader in this card; the ruling places reader (2) on the plugin-approvals card and the ledger row says so. Acceptable under the ruling, on the condition the seat already recorded on plugin-approvals: sys_approval_request declares its per-caller viewer block under attachedOnRead, with a conformance test against attachViewers (#22211 ruling A, plugin-approvals half) #22387: its conformance test pins the keys AND the runtime value types. Until it lands, the types are a declaration awaiting its named reader.

Round-2 open_questions: none.

Dev flags (PR body and report 6075183735):

Reviewer's flags:

Check-runs on the head at this reading:

  • Red: Test Core (6/6) — this diff's, judged in ①.
  • Pending, not read as green: Test Core (1/6), (2/6), (3/6), (4/6). Whatever they conclude, this head cannot land on the red above.
  • Green: Build Core; Build Docs; Check Changeset; Check PR Size; Dogfood Regression Gate (the rollup and shards 1/3, 2/3, 3/3); Dogfood Verify CLI; Governed Surface Queue Guard; Lint & Repo Gates (green on this head — the step ci(pm): check:pm-dispatch-gates is red on main — its live-tree case reads mkdtempSync(join(process.cwd(), …)) in a #22365 dogfood test as an unresolved temp base #22422 tracks did not red here); Spec property liveness; Temporal Conformance (live PG + MySQL); Test Core (5/6); TypeScript Type Check (the rollup) with Type Check · source gates, Type Check · consumer gates, Type Check · debt ledger, Type Check · workspace; the four card and branch guards; Auto Label; Check Documentation Links; Flag docs affected by code changes; filter.
  • Skipped: Console Pin Gate (no export removed, no pin moved — the skip is right); Packed-tarball smoke (opt-in).

Remedy, one round: (1) CARD_PROPERTY_COUNTS.object 44 → 45 in protocol.meta-types-degenerate-derivation.test.ts, with the docblock sentence the file's pattern asks for ("object moved 44 → 45 the same way when it gained attachedOnRead"); (2) the seat amends the claim by that file; (3) metadata-protocol's tier runs in full at the new head and the report says so; (4) a new ## Contract review record on the new head — this one names 4ba858882 only.

Implemented-by: claude/issue-22386-attached-on-read
Reviewed-by: session_01DhTqaEHqPVSVnAkjG3jywn

VERDICT: FAIL

claude added 3 commits October 9, 2026 06:15
…in; name the build validator as the reader

The served object schema gains attachedOnRead, so CARD_PROPERTY_COUNTS.object
moves 44 to 45 with the docblock sentence its precedents carry. The
describe text, docblock, ledger note and changesets now name the shared
build validator (lint over formula) as the reader, and record that the
injected-column collision is out of reach of object.zod.ts.

Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a0545b853fc24b707a9433380cbea7b60ed973f2
Local-runs: none

Reviewer seat session_01DhTqaEHqPVSVnAkjG3jywn, reading at 2026-10-09T07:42Z. Inputs: card #22386 (body and every comment; the ruling 6070963704 on #22211 and the FAIL record 6075426209 through the card's references), PR #22425 (body, the 22-file list, the net diff from merge base 3054516ef to the head), and the head's check-runs. Read-only: the head was fetched into a private ref; nothing was built, run or re-run. The one measurement beyond git diff was a dry-run git merge-tree of the head against current main on the fetched refs, which touches no working tree (F5 in ③).

One line: PASS on this head. The sixth closed-world pin is visited, the reader wording is narrowed to the one door delivered, every check-run on the head is completed and none is red, and an independent inventory finds no seventh pin. One landing precondition, not a contract defect: main has moved on the lint test file since this head was cut, and the merge needs one hunk resolved (F5).

① Derived judgments

This head differs from 4ba858882 (the FAIL head) by three commits: a main merge at 3054516ef (clean, through os-regen-merge.sh), 3b09ab36a (the pin and the wording) and a0545b853 (reference docs regenerated). Everything the FAIL record judged right in ① is unchanged in the net diff and is carried here as right: the ObjectSchemaBase key attachedOnRead (a strict record of strict records; both key levels under the field-name grammar; each leaf one of FieldType.extract over number, text, boolean, date; optional; absence materialises no key; ObjectSchema.create() takes it); the five parse refusals, the last two in the object's existing superRefine at ['attachedOnRead', block]; ObjectExtensionSchema unchanged; composeStacks deriving the key into its merge-refusal set from the shape walk with stack.zod.ts untouched; the generated artifacts by generators only; the liveness row live on an authoring consumer; no spec export added or removed. In the three other packages: formula's ExprSchemaHint.attachedOnRead? and the unknown-field params block? + leaves? (present together exactly when the message carries the leaves clause), the second segment judged by the sticky SECOND_SEGMENT_RE with RECORD_REF_RE untouched, under the existing unknown-field code, nothing keyed on viewer; lint's buildFieldIndex appending the declared block names and buildAttachedOnReadIndex threaded into both call sites that pass the field index; metadata-core's OBJECT_REFERENCE_POSITIONS.attachedOnRead: keep; the form ledger's root omit row. Right, each.

Judged new on this head:

  • The sixth pin, visited. Right. packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts: CARD_PROPERTY_COUNTS.object 44 → 45, and the docblock gains the one sentence the file's pattern asks for, beside the imageField and print precedents. The served object card's 45 top-level properties agree with the 45 data/Object: ids in authorable-surface/data.json at the head. Test-only: no changeset is owed, and @objectstack/metadata-protocol is otherwise untouched. Test Core (6/6) is success on this head where it was failure on 4ba858882.
  • The reader wording, narrowed. Right. The .describe() text (and so the three regenerated reference pages), the AttachedOnReadSchema docblock, the liveness note and both changesets now name the reader as the shared BUILD validator (@objectstack/lint's expression rule over @objectstack/formula, as os build / os validate run it); the spec changeset adds "No other field-existence check reads it"; the docblock and the ledger note name the two F1 doors (packages/mcp's expression tool and service-automation's flow-registration resolver) as carried by plugin-approvals: sys_approval_request declares its per-caller viewer block under attachedOnRead, with a conformance test against attachViewers (#22211 ruling A, plugin-approvals half) #22387. The .describe() text and the refusal strings carry no tracker number. One string the round did not reach, a nit and not published text: the form-ledger omit row's why in metadata-form-zod-reconciliation.test.ts still says "its one reader is the expression validator" (F4).
  • F2, measured and left, as the order allowed. Right. Verified at the head: packages/spec/src/data/injected-system-columns.ts:69 imports isTenancyDisabled from ./object.zod, so the reverse import is a cycle, and PRIMARY_KEY_COLUMN, OWNER_COLUMN, OWNING_BUSINESS_UNIT_COLUMN and TENANT_SCOPE_COLUMN are module-private consts at :79–:86. refuseAttachedBlockConflicts is unchanged and the docblock records the measurement. The residue is confined to the validator (a block named like an injected column parses clean and shadows that column's second segment); no served row and no stored shape changes.
  • The closed-world inventory, re-done independently at the head: no seventh pin. A per-file scan of the tree for the 23 rarely co-listed ObjectSchema top-level keys, and for every count or key-set expression over the shape, excluding the 21 files that already name attachedOnRead, finds nothing unvisited:
    • the dev's eight visited lists and counts ([a]–[h] in report 6076426705) are the only closed-world lists or counts of the key set. The two numeric hits outside them are fixtures, not pins: packages/spec/scripts/liveness/readme-table.test.ts:534 (classified: { object: 50 } inside the synthetic HONEST report) and packages/spec/src/contracts/analytics-service.test.ts:130 (object: 42, a @ts-expect-error type pin on AnalyticsResult.object);
    • the validate-expressions / validate-security-posture 也有同形的 spec 不声明键的 ?? 别名读法(#5009 建议 3 的核对结果) #5017-style meta-guards that read Object.keys(ObjectSchema.shape) outside the expression rule (validate-org-axis-red-lines.test.ts:339, validate-rule-compilability.test.ts:581 and :797, validate-security-posture.test.ts:1462, :1464 and :1614) pin the subset THEIR rule reads; none of those rules reads the new key, so none moves;
    • derived, needing no edit: authorable-surface.base.json (the frozen base its generator alone writes; check:authorable-surface runs inside the green Type Check · source gates), the undrilled-containers baseline under packages/spec/scripts/liveness/ (the key is a dynamic record with no static child property, so it is not a drillable container; Spec property liveness is green), the four generated form translation bundles and the two form echo-decision tests under platform-objects (form-derived; the key is an omit row), and validate-object-field-refs.ts (pointer keys only);
    • not pins, and not owed by the card: the hand-written content/docs/data-modeling/objects.mdx and the skills/objectstack-data references list keys by example.

Reach of reader 1. Unchanged from the FAIL record: lint's buildFieldIndex is the one door the card names; the two other record.* field-set builders do not read the key and reach nothing until #22387 declares viewer. F1 in ③.

② Semver level

Packages whose published files move: @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/metadata-core — all public, all at 17.7.0. Two changesets cover exactly those four; no skip-changeset; Check Changeset is green on both of its runs. @objectstack/metadata-protocol moves a test file only and owes none.

Clause-②: yes (widening: a new optional ObjectSchema key) — identical in the PR body (its third line) and in the spec changeset; (widening) is one arm of the closed pair; no major level anywhere.

③ Boundary flags

The seat order 6075451490 on the FAIL record, item by item: (1) the sixth pin — visited, judged in ①; (2) F2 — measured and left within the order's wording, verified in ①; (3) metadata-protocol's tier in full, the inventory grep and the re-derived gate list — report 6076426705 states each (223 of 226 files with 3 env-gated live-DB skips; 111 derived, 111 run, 0 NOT-MEASURED), and the head's check-runs are the gate verdicts this record reads; (4) the PR body's "Measured" section is at a0545b853, and the body edit is the seat's. Each answered.

Round-1 open_questions (report 6074289657) → seat order 6074312931, A, A, A; re-judged by the FAIL record and unchanged here: Q1 is now complete with the sixth file; Q2 agreed; Q3 agreed, with (iii) still conditional on #22387's conformance test reading the leaf TYPES — the ledger row says so in words. Round-2 and round-3 open_questions: none.

Dev flags (PR body and reports 6075183735, 6076426705):

Reviewer's flags:

Check-runs on the head at this reading — 42, every one completed:

  • Red: none.
  • Pending: none. (Five app check-suites — vercel, fly-io, claude, cloudflare-workers-and-pages, objectstack-fleet — sit queued with zero check-runs, their standing empty state; there is no check of theirs to read.)
  • Green (38): Build Core; Build Docs; Check Changeset (both runs); Check Documentation Links; Check PR Size; Dogfood Regression Gate (the rollup and shards 1/3, 2/3, 3/3); Dogfood Verify CLI; Flag docs affected by code changes; Governed Surface Queue Guard; Lint & Repo Gates; Spec property liveness; Temporal Conformance (live PG + MySQL); Test Core (the rollup and all six shards, 6/6 included); TypeScript Type Check (the rollup) with Type Check · source gates, Type Check · consumer gates, Type Check · debt ledger, Type Check · workspace; Auto Label; filter; and the four card and branch guards (No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch), each green on both runs.
  • Skipped (4): Console Pin Gate (no export removed, no pin moved — the skip is right); Packed-tarball smoke (opt-in); and, on the second run of the PR-metadata workflow after the PR body edit at 2026-10-09T07:27Z, Auto Label and Check PR Size, both green on the first run. The commit status rollup is success.

Implemented-by: claude/issue-22386-attached-on-read
Reviewed-by: session_01DhTqaEHqPVSVnAkjG3jywn

VERDICT: PASS

claude added 2 commits October 9, 2026 07:44
…tached-on-read

# Conflicts:
#	packages/lint/src/validate-expressions.test.ts
… validator as its reader

Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e19c1e349d35dca7d8cef7ce405a421626b54200
Local-runs: none

Reviewer seat session_01DhTqaEHqPVSVnAkjG3jywn, reading at 2026-10-09T09:03Z. Inputs: card #22386 (body and every comment, the rulings and the earlier records included), PR #22425 (body, the 22-file list, the net diff from merge base 05c7c3fa3 to the head), and the head's check-runs. Read-only: the head was fetched into a private ref; nothing was built, run or re-run. The three measurements beyond git diff are reads of fetched refs: a merge-base and ancestry query, an interdiff of the PR's own added and removed lines before and after the merge round, and a per-file intersection of main's later commits with the PR's files. The red shard's job log could not be read (its log host is outside the read proxy); the job's step list and annotations, and main's own runs of the same shard, were read through the GitHub REST API.

One line: PASS on the contract. This head is the PASS head a0545b853 plus one merge of main and the F4 string, and the PR's own delta is byte-identical apart from that string; #22394's member verdicts and this PR's field-index threading coexist correctly in validate-expressions.ts. The required Test Core gate is RED on this head, and the red is not this diff's: the shard's test step is green, the red step is the #22415 timing-drift grade, which main itself tripped on the same step and has since reverted. The head cannot land as it stands (F6, escalated to the owning seat).

① Derived judgments

The PR's own delta, before and after the merge round — unchanged apart from F4. Right. The added and removed lines of the diff against the merge base, over the 22 PR files, are 764 at a0545b853 (base 3054516ef) and 764 at e19c1e349 (base 05c7c3fa3), and they differ in exactly one line: the form-ledger omit row's why, which now ends "its one reader is the shared build validator" (F4). The diffstat is the same 22 files, +753 / −11. Between the two bases main moved 65 files, two of them this PR's (validate-expressions.ts, validate-expressions.test.ts); the 63 others entered the head through the merge commit 37cc130bd alone and are main's, not this diff's. Everything the PASS record 6076679507 judged right in ① — the ObjectSchemaBase key attachedOnRead as a strict record of strict records under the field-name grammar with the four Field.returnType leaf types; the five parse refusals, two of them in the object's existing superRefine at ['attachedOnRead', block]; ObjectExtensionSchema unchanged; composeStacks deriving the key into its merge-refusal set with stack.zod.ts untouched; the generated artifacts by generators only; the liveness row live on an authoring consumer; no spec export added or removed; formula's ExprSchemaHint.attachedOnRead? and the unknown-field params block? + leaves? under the existing code with RECORD_REF_RE untouched; lint's buildFieldIndex and buildAttachedOnReadIndex; metadata-core's attachedOnRead: keep; the sixth pin CARD_PROPERTY_COUNTS.object: 45; the narrowed reader wording — is carried here as right, read again in the net diff.

The one conflict, the #5017 PLUMBING list in validate-expressions.test.ts. Right. Both sides are kept: main's five #22394 names (declaredUserMembers, boundUserMembers, listedNames, tickedNames, membersRead) with main's comment, then attachedOnReadIndex with this PR's comment. In the net diff the five names are context and the PR adds exactly what it added before the merge, so the resolution contributed no line of its own. The READ_SURFACES obj row still reads ['actions', 'attachedOnRead', 'fields', 'name', 'validations']. No conflict marker in either lint file.

Coexistence in validate-expressions.ts. Right. The file's diff against main is exactly this PR's hunks and nothing else: the docblock row, buildFieldIndex appending the block names after the injected columns, the three helpers (attachedOnReadOf, attachedBlockNames, buildAttachedOnReadIndex), the index built beside fieldIndex, and the two call sites; 13 mentions of attachedOnRead, zero markers. #22394's verdict — optionVisibleWhenMemberIssue, optionVisibleWhenUserMembers, membersReadUnder, membersReadThrough, tickedList — reads EvalUserSchema.shape, buildScope's current_user, and two static maps (OPTION_VISIBLE_WHEN_BOUND_MEMBERS, ctx and os bound to user; OPTION_VISIBLE_WHEN_USER_ROOTS, current_user and user). It never consults the field index, and the field index feeds only the record and previous roots through ExprSchemaHint; the two passes share no root. Of the four validateExpression( call sites at the head, the two that pass fields (the predicate check closure and the field-formula judge) also pass attachedOnRead, and the two others pass no hint at all, so no field-existence judgement runs there: "both call sites that pass the field index" holds on the merged file. The third fieldIndex.get consumer, shadowedFieldReads (#14089), predates both changes and now sees block names — the accepted out-of-scope finding's class, reach-less today. The #5017 meta-guard tables both sets.

F4. Right. A test-only string in metadata-form-zod-reconciliation.test.ts; no changeset owed; no published text moves.

The merged tree's generated artifacts. Right. main moved none of this PR's generated files, so os-regen-merge.sh took main's side only where only main moved; Type Check · source gates (check:generated --reconcile-only, check:authorable-surface, check:docs) and Spec property liveness are green on this head, which is the joint verdict on the merged sources.

metadata-protocol and metadata-core on the merged tree. Right, with one wording nit. main moved 14 files under those two packages between the bases (#22401 and its tests), none of them this PR's; the sixth pin is in the head unchanged, and every shard's Run this shard's tests step is success on this head, which is the verdict on the merged tree. The report's "the merge did not touch them" is true of this PR's files, not of the packages — a nit.

The gate verdicts this record reads. Test Core (6/6) is failure on the head and the rollup Test Core is failure with it. The shard's steps: Run this shard's tests success, Test completeness guard success, Check this shard's timing drift failure (Process completed with exit code 1), after which the attestation pair is skipped and the rollup's Verify test shard results fails on the missing attestation — the fail-closed direction ci.yml documents. That step is partition-test-shards.mjs --check-drift, the measured-over-predicted ratio of the shard's executed test windows; it reds past 1.5x and reads no test outcome. The grade was changed by #22415 (c64130baf, in the main this round merged). main itself tripped it: at 081e6a09d, Test Core (6/6) job 113726697133 red on the same single step, and a later run of the same shard on the same commit went green (job 113728612630); main then reverted #22415 at 806b03e2a (#22435), the newest main commit at this reading, touching ci.yml and partition-test-shards.mjs only. Judged: the gate is red; the red is not this diff's and not a test outcome, and the head cannot land on it as it stands — F6 in ③. Not read as green.

② Semver level

Unchanged from the PASS head: the two changesets are byte-identical at e19c1e349 (no changeset line is among the one-line interdiff). Packages whose published files move: @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/metadata-core, all public; @objectstack/metadata-protocol moves a test file only and owes none. No skip-changeset; Check Changeset is green on both of its runs on this head.

Clause-②: yes (widening: a new optional ObjectSchema key) — identical on the PR body's third line and in the spec changeset; (widening) is one arm of the closed pair; no major anywhere in this PR's changesets. The main this round merged carries its own breaking changesets (#22401, #22427); they are main's files, not this PR's declaration.

③ Boundary flags

Dev flags this round (report 6077664292, the PR body's "Merge round" section):

  • The merge through os-regen-merge.sh, never rebased, one conflict kept both sides — right, judged in ①.
  • F4 fixed, test-only, no changeset — right, resolved on this head.
  • origin/main moved after the push and the queue rebuilds onto main — right as far as it goes: the two later commits (081e6a09d, 806b03e2a) touch none of the 22 files, and 05c7c3fa3 is the merge base. But the second of them is the revert of ci(test-shards): grade the Test Core split on predicted shard wall and slice the CLI per run #22415, which is what reds this head — F6.
  • metadata-protocol and metadata-core "untouched by this merge" — a wording nit, judged in ①; the merged tree's test steps are green.
  • The PR body was not edited by the dev; the seat's "Merge round at e19c1e349" section is present and matches the report's measured lines.

Earlier flags, each re-read on this head: F1 carried (the MCP expression tool and the automation flow-registration resolver build record.* sets from fields alone; every published string names only the shared build validator; carrier #22387, outside this record's inputs, taken as stated); F2 noted (the injected-column collision; measured, the docblock records the cycle); F3 noted (ObjectExtensionSchema); F4 resolved here; F5 resolved here — the merge the PASS record foresaw is done, with exactly the one hunk it named, resolved as it said. open_questions in rounds 2, 3 and 4: none. The seat orders 6074312931 and 6075451490 stay answered as the PASS record found them.

Reviewer's flags:

Check-runs on the head at this reading — 42, every one completed:

  • Red (2): Test Core (6/6), Test Core — judged in ① and F6.
  • Pending: none. (Five app check-suites — vercel, fly-io, claude, cloudflare-workers-and-pages, objectstack-fleet — sit queued with zero check-runs, their standing empty state; there is no check of theirs to read.)
  • Green (36): Build Core; Build Docs; Check Changeset (both runs); Check Documentation Links; Dogfood Regression Gate (the rollup and shards 1/3, 2/3, 3/3); Dogfood Verify CLI; Flag docs affected by code changes; Governed Surface Queue Guard; Lint & Repo Gates; Spec property liveness; Temporal Conformance (live PG + MySQL); Test Core (1/6), (2/6), (3/6), (4/6), (5/6); TypeScript Type Check (the rollup) with Type Check · source gates, Type Check · consumer gates, Type Check · debt ledger, Type Check · workspace; filter; the four card and branch guards (No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch), each green on both runs; Auto Label and Check PR Size, green on their first run.
  • Skipped (4): Console Pin Gate (no export removed, no pin moved — the skip is right); Packed-tarball smoke (opt-in); and Auto Label and Check PR Size on the second run of the PR-metadata workflow, both green on the first. The commit status rollup reads success; the required-context picture is the check-runs above, with Test Core red.

Implemented-by: claude/issue-22386-attached-on-read
Reviewed-by: session_01DhTqaEHqPVSVnAkjG3jywn

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a5104d85433bdf7511d561694ebb0910750c488d
Local-runs: none

Reviewer seat session_01DhTqaEHqPVSVnAkjG3jywn, reading at 2026-10-09T10:54Z. Inputs: card #22386 (body and every comment, the rulings and the earlier records included), PR #22425 (body, the 22-file list, the net diff from merge base 2b61f2d9d to the head), and the head's check-runs. Read-only: the head was fetched into a private ref; nothing was built, run or re-run. The measurements beyond git diff are reads of fetched refs: merge-base and ancestry queries, an interdiff of the PR's own delta before and after the two merges, per-file intersections of main's moves with the PR's files, and a marker and call-site grep of the merged lint files; the shard's step list came through the GitHub REST API.

One line: PASS on this head. It is the PASS head e19c1e349 plus two merges of main and nothing else: the PR's own delta is unchanged line for line; #22315's text-slot pass and this PR's attachedOnRead field-index threading coexist in validate-expressions.ts without sharing a root, an index or a file region; the required Test Core context is green, the #22435 revert being an ancestor of the head; every check-run on the head is completed and none is red or pending. The previous record's landing precondition (F6) is resolved, and no new flag blocks.

① Derived judgments

The PR's own delta, before and after the merge round — unchanged. Right. The diff from the merge base over the PR's files is 22 files, +753 / −11, at e19c1e349 (base 05c7c3fa3) and at a5104d854 (base 2b61f2d9d). The two patches, 1084 lines each, differ only in index blob lines and in the hunk-header line numbers of the two lint files, shifted by #22315's insertions; no added or removed line differs. Between the two bases main moved 119 files (+4906 / −1484), and git diff e19c1e349 a5104d854 is exactly those 119 files with the same counts, so the two merge commits (b5b733756 onto da159f74e, a5104d854 onto 2b61f2d9d) contributed no line of their own and dropped none of main's. Of the 119, two are this PR's files, both moved by #22315: validate-expressions.ts and validate-expressions.test.ts; the 117 others entered through the merge commits alone and are main's. Everything the records 6076679507 and 6077844912 judged right in ① — the ObjectSchemaBase key attachedOnRead as a strict record of strict records under the field-name grammar with the four Field.returnType leaf types; the five parse refusals, two of them in the object's existing superRefine at ['attachedOnRead', block]; ObjectExtensionSchema unchanged; composeStacks deriving the key into its merge-refusal set with stack.zod.ts untouched; the generated artifacts by generators only; the liveness row live on an authoring consumer; no spec export added or removed; formula's ExprSchemaHint.attachedOnRead? and the unknown-field params block? + leaves? under the existing code with RECORD_REF_RE untouched; lint's buildFieldIndex and buildAttachedOnReadIndex threaded into both call sites that pass the field index; metadata-core's attachedOnRead: keep; the sixth pin CARD_PROPERTY_COUNTS.object: 45; the narrowed reader wording; the F4 string — is carried here as right, read again in the net diff.

Coexistence with #22315 in validate-expressions.ts. Right. #22315 added two imports from @objectstack/spec/automation (flowNodeTextSlotSources, textSlotTemplateRefusal) and one pass inside the flow-node loop (:2248–:2258 at the head): for each text slot of a node, textSlotTemplateRefusal(slot.source), then validateExpression('template', slot.source) with NO schema hint. In formula, checkFieldExistence returns at once when the hint carries no fields, so neither the field index nor the attachedOnRead hint can reach that pass; and the pass reads the node's cfg, never fieldIndex or attachedOnReadIndex. This PR's hunks — the docblock row, buildFieldIndex (:161), the three helpers (:204–:235), the index built beside fieldIndex (:1706), the predicate check closure (:1859–:1861) and the field-formula judge (:2486–:2487) — all sit outside that loop body. Of the five validateExpression( call sites now in the file, the two that pass fields (:1860, :2482) are the two that pass attachedOnRead; the three others (:1954, :1982, #22315's :2255) pass no hint, so no field-existence judgement runs there: "both call sites that pass the field index" holds on the merged file. 13 mentions of attachedOnRead, zero conflict markers in either lint file. In the test file, the #5017 PLUMBING list reads templateRefusal, then #22315's slot, then #22394's five names, then attachedOnReadIndex, each under its own comment, and the READ_SURFACES obj row still reads ['actions', 'attachedOnRead', 'fields', 'name', 'validations']. In formula, #22315 moved template-engine.ts and its test only (PATH_ONLY_RE admits $); validate.ts is untouched by main, and its blob at the head is f7e24675, the ablated blob. The two passes share no root, no index and no file region.

The merged tree's generated artifacts. Right. main moved none of this PR's generated files between the bases (the only overlap is the two lint files), so os-regen-merge.sh took main's side only where only main moved. Type Check · source gates (check:generated --reconcile-only, check:authorable-surface, check:docs) and Spec property liveness are green on this head, the joint verdict on the merged sources.

The gate verdicts this record reads. Test Core is green on this head. Test Core (6/6) is success, and its step list reads Run this shard's tests, Check this shard's timing drift, Test completeness guard and the attestation pair all success; the rollup Test Core is success with Verify test shard results success. The #22435 revert (806b03e2a) is an ancestor of the head, verified, so the PR's CI ran the pre-#22415 step — the remedy the previous record named. The red on e19c1e349 is gone the way that record said it would go; no source, test, changeset or artifact in this PR moved for it.

② Semver level

Unchanged from the two PASS heads: the two changesets are inside the unchanged delta and byte-identical. Packages whose published files move: @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/metadata-core, all public; @objectstack/metadata-protocol moves a test file only and owes none. No skip-changeset; Check Changeset is green on both of its runs on this head.

Clause-②: yes (widening: a new optional ObjectSchema key) — identical on the PR body's third line and in the spec changeset; (widening) is one arm of the closed pair; no major anywhere in this PR's changesets. The main this round merged carries its own breaking changesets (#22315, #22436, #22439); they are main's files, not this PR's declaration.

③ Boundary flags

Dev flags this round (report 6079342728; the PR body's "Merge round at a5104d854" section):

Earlier flags, each re-read on this head: F1 carried (the MCP expression tool and the automation flow-registration resolver build record.* field sets from fields alone; every published string names only the shared build validator; carrier #22387, outside this record's inputs, taken as stated); F2 noted (the injected-column collision; measured, the docblock records the cycle; carrier none); F3 noted (ObjectExtensionSchema cannot carry the key; in the Acceptance notes); F4 and F5 resolved at e19c1e349; F6 resolved on this head — the revert is an ancestor and the drift step is green, judged in ①. The seat orders 6074312931 and 6075451490 stay answered as the PASS records found them.

Reviewer's flags:

Check-runs on the head at this reading — 42, every one completed:

  • Red: none.
  • Pending: none. (Five app check-suites sit queued with zero check-runs, their standing empty state; there is no check of theirs to read.) At this seat's first read the second run's Check Changeset was still in_progress; it completed success before this record was written, and the record reads it as green only on that completion.
  • Green (38): Build Core; Build Docs; Check Changeset (both runs); Check Documentation Links; Dogfood Regression Gate (the rollup and shards 1/3, 2/3, 3/3); Dogfood Verify CLI; Flag docs affected by code changes; Governed Surface Queue Guard; Lint & Repo Gates; Spec property liveness; Temporal Conformance (live PG + MySQL); Test Core (the rollup and all six shards, 6/6 included); TypeScript Type Check (the rollup) with Type Check · source gates, Type Check · consumer gates, Type Check · debt ledger, Type Check · workspace; filter; the four card and branch guards (No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch), each green on both runs; Auto Label and Check PR Size, green on their first run.
  • Skipped (4): Console Pin Gate (no export removed, no pin moved — the skip is right); Packed-tarball smoke (opt-in); and Auto Label and Check PR Size on the second run of the PR-metadata workflow, after the PR body edit, both green on the first run.

Implemented-by: claude/issue-22386-attached-on-read
Reviewed-by: session_01DhTqaEHqPVSVnAkjG3jywn

VERDICT: PASS

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

Projects

None yet

2 participants