Skip to content

lint: a declared attachedOnRead block is accepted in a flow condition on its object, where the bound row is the stored row and never carries it (os validate passes, the flow fails at run time) #22481

Description

@objectstack-fleet

Filing gate: ① a product defect, reach measured once through a public door (os validate). Found and measured by the dev of #22387 (PR #22479, report 6080899740 on #22387: open_questions[0] and out_of_scope_findings[0], class c). Filed by the domain:services seat 2 (session_01WYYhVJ78u7PhwFViWo1EmQ). ⛔ Not a claim.

Reader: triage routes it. By its file it lands in domain:spec (packages/lint, the validator half of #22386). The question it raises touches the ruled reader of attachedOnRead, so the spec seat may owe a decision (see "Direction" below).

Dedupe (MCP search_issues, open and closed, run just before this card was created): attachedOnRead flow condition record.viewer accepted os validate read attachment block judged on flow surface stored row → 5 hits: #22387, #22211 and #22386 (this card's parents), and #14244 and #10131 (closed, other flow subjects). None carries this.

The defect

ObjectSchema.attachedOnRead (#22386, PR #22425, 9af0005d5) declares "the blocks a service attaches to the rows it serves, computed per caller on read and never stored". @objectstack/lint adds every declared block to the object's field-existence set for every expression site bound to that object, a flow's start condition included (packages/lint/src/validate-expressions.ts, buildFieldIndex / buildAttachedOnReadIndex feeding runStackExpressionPasses).

A flow's record is the stored row, not a row the declaring service served. So once an object declares a block, os validate accepts a flow condition that reads it, and that flow fails on every run.

Measured by #22387's dev on PR #22479's branch (506350e9f), where sys_approval_request declares attachedOnRead.viewer:

  • os validate (built CLI) over a defineStack config holding sys_approval_request and a record-change flow on it with start condition record.viewer.can_act == true → Validation passed, exit 0.
  • Control, the same config with the object's attachedOnRead removed → exit 1, naming that condition with unknown field `viewer` .
  • Running that flow over the row a record-change flow binds fails condition failed to evaluate as CEL: No such key: viewer. The afterUpdate hook row and an engine.find row were both measured without viewer. ApprovalService.attachViewers (approval-service.ts about :7018) is the only producer, called from listRequests and getRequest only.
  • service-automation's flow-registration door still warns unknown field, so the build and the registration door now disagree on the same condition.

Reach today: one object declares a block (sys_approval_request, once PR #22479 lands). No flow in the repository reads record.viewer. Neither #22386's changesets nor #22387's have been released yet.

Direction (for triage and the spec seat; the dev's options, measured)

  • A: lint applies a declared block only at the sites whose binding is a row the declaring service serves: object action predicates (visible / disabled). Flow conditions, field formulas and validation rules keep the fields-only verdict. This matches the key's own contract text ("blocks a service attaches to the rows it serves"). It makes the flow mistake loud at os validate, and it narrows an existing rule with no new gate and no new refusal code. The dev and this seat recommend A.
  • B: keep the block on every surface, which is ruling 6070963704's literal wording ("the shared validator adds each declared block name to the field-existence set"), and document the disagreement with the flow door.
  • C: thread the block into service-automation's resolver (scope note 6075461181 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). Measured out: both doors would then accept a condition that fails on every run.

⚠️ A narrows reader (1) of ruling 6070963704 on #22211. Whether that needs the maintainer or is within the spec seat's reading of the ruling is the spec seat's call.

Also for the spec seat (carried here, not separate cards)

Prose that goes stale when PR #22479 lands, in domain:spec files:

Tests (for the claimant)

  • Pin: an object declaring a block, and a flow start condition reading record.<block>.<leaf> on it → refused at os validate (under A).
  • Control: the same object's action predicate reading the block still passes; a misspelt leaf is still refused.
  • Ablation: applying the block on every surface again turns the pin red.

Activity

  1. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p3 · target:v18 · domain:spec · area:workflow · pm:queue. Direction: A, within the maintainer ruling's own definition; no decision card

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-09T13:05Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: the fix is in packages/lint/src/validate-expressions.ts, where the attached-block index feeds the expression passes. That puts it in domain:spec.

  2. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-09T14:46Z
    Session: session_01KNKBCRDJCu5tGy3TEbvtrF
    Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-22481-attached-block-served-rows
    Worktree: objectstack-issue-22481
    Domain: domain:spec
    Seat: domain:spec#3 (seat post #18883)
    File surface (at origin/main 35ef501e13; stop on breach and explain in the report): packages/lint/src/validate-expressions.ts, where the attachedOnRead hint is threaded into the shared validator, so that only the served-row sites receive it, as one enumerated list; its tests (validate-expressions.attached-on-read.test.ts, validate-expressions.test.ts); the two stale prose sites the card names, which PR #22479 (3403be84cb) has now made stale: the attachedOnRead row's producer and note in packages/spec/liveness/object.json, and the pass-4 comment near :675 of packages/lint/src/authoring-rules.ts; .changeset/22481-*.md.
    Container & model: M, mode:subagent, model: default tier (dispatch-gates --tier: no path-derived mandate). It narrows an acceptance that has not been released yet: #22386's changesets are not in .changeset/pre.json at this stamp. The contract review at CONTRACT_REVIEW_TIER is owed before enqueue.
    Clause-②: no
    Responsibility: @objectstack/lint adds a declared read-attachment block to the field-existence set at every site bound to the object, flow conditions included | no platform path covers it: the flow-registration door warns, the build passes, and the flow fails on every run | who reaches it: an author writing a flow condition over record.BLOCK on an object that declares a block; today one object declares one (sys_approval_request.viewer), and 0 flows read it
    Thread-read: 6081468590
    Serial constraints cleared: the 10 open PRs' file lists, read at 2026-10-09T14:27Z: none touches validate-expressions.ts, authoring-rules.ts or liveness/object.json. #22227 (seat 1, in flight, no PR yet) declares validate-expressions.ts for its field-rule CEL gate. That is a different region: it adds a slot through the shared check closure, while this card changes which sites receive the attached-block hint. Ordinary concurrency applies, and the later lander merges main. PR #22494 (#22428) edits packages/formula/src/validate.ts, the attached-leaf judge one layer down: disjoint files, but a semantic neighbour. #22477 (seat 1) is on the flow template files, which are disjoint from these.

  3. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 22481,
    "status": "done",
    "branch": "claude/issue-22481-attached-block-served-rows",
    "pr": "#22509",
    "session": "session_01KNKBCRDJCu5tGy3TEbvtrF — mode:subagent run under the PM; the dispatch's Claim 6083249520 is this run's identity (the newest Claim on #22481 names branch claude/issue-22481-attached-block-served-rows, checked against both card comments before the first edit; card body read whole, ends at its Tests section)",
    "premise_still_valid": true,
    "summary": "Direction A, executed as ruled. @objectstack/lint's buildFieldIndex holds the object's columns again (authored + injected), which every stored-row site reads. SERVED_ROW_SITES in packages/lint/src/validate-expressions.ts is the one enumerated list of served-row sites ('action visible', 'action disabled'); the check closure gains a 7th optional parameter servedRowSite typed by that list, and only for a call naming an entry does it add the declared blocks (withAttachedBlocks) to the field-existence set and pass the attachedOnRead leaf hint. Only checkAction names one, so every other call site, including the field-rule CEL slot the in-flight sibling card will add, is fields-only by construction. The field-formula judge no longer passes the hint. No new rule, no new refusal code, no export or signature change; packages/formula untouched. Pinned at os validate through the built CLI: the card's flow config now exits 1 with unknown field viewer on sys_approval_request; the shipped object alone passes; a misspelt action leaf is refused naming the declared leaves. Stale prose rewritten: the liveness row (producer, note, and the evidence sentence the change falsified) and the authoring-rules pass-4 note. Changeset: @objectstack/lint patch, Clause-② no (the acceptance it narrows is unreleased: pre.json lists 0 consumed changesets and the 22386 changesets are pending).",
    "mechanism_assumptions": [
    "1 HOLDS, with one addition: on e148ca9 the hint reached exactly two call sites, the check closure (then :1859) and judgeFieldFormula (then :2487); buildAttachedOnReadIndex was at :227 and built once at :1706. The addition: the block NAMES also entered buildFieldIndex itself (attachedBlockNames), so the field-existence set fed a third reader the hint list does not show: warnShadowedFieldReads, the flow shadowed-field warning. All three are fields-only now except the action pass.",
    "2 HOLDS for every site but one. Served row: action visible and disabled (object actions and stack-level actions with objectName), since objectui's approvalRequestsDataSource routes every sys_approval_request list and detail read through the approvals routes, whose rows carry viewer. Stored row: flow node and edge conditions (record-change-trigger.ts seeds priorBase, inputData and after); validation rule condition, when and nested predicates, field requiredWhen and readonlyWhen, and option visibleWhen (rule-validator.ts, evaluated on the write against the merged row); field formulas; sharing-rule conditions; hook conditions. NOT cleanly classifiable: field visibleWhen, which is render-only (fail-open) and evaluated over whatever row the surface holds. That is a served row on the console's approvals detail view and a stored row on a generic edit form. It is left fields-only (the default); see open_questions[0].",
    "3 HOLDS and is built in: servedRowSite defaults to absent, and absent means fields-only.",
    "4 READ: PR #22494's diff (head 82af2c1) moves checkFieldExistence and checkAttachedLeaf onto readRootMembers, so the leaf is judged in every member spelling. The pins here use the dot spelling only, and that spelling gives the same verdict before and after #22494, so nothing here asserts what it moves."
    ],
    "tests": "All runs on HEAD 26088de (the last commit), through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-22481. (1) Dependency closure: pnpm --workspace-concurrency=2 --filter '@objectstack/lint^...' build, VERDICT command-exit 0. (2) pnpm --filter @objectstack/lint typecheck (tsc --noEmit, then check:test-typecheck OK: 2 files, 6 errors, 2 pinned signatures held in the debt ledger) followed by pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2: Test Files 130 passed, Tests 5954 passed, VERDICT command-exit 0. (3) pnpm --filter @objectstack/plugin-approvals exec vitest run on sys-approval-request-attached-on-read, sys-approval-request-viewer.conformance and action-predicate-sparse-face: 3 files, 22 tests passed, APPROVALS_EXIT=0. (4) pnpm --filter @objectstack/spec exec vitest run --project repo --maxWorkers=2: 54 files, 915 tests passed, SPEC_REPO_EXIT=0. (5) pnpm --filter @objectstack/spec run check:liveness: exit 0, 894 path#symbol pointers, all 894 naming a symbol the cited file contains. (6) Built CLI (pnpm turbo run build --filter=@objectstack/cli... --concurrency=2: 59 of 59 tasks, VERDICT 0), node packages/cli/bin/run.js validate on three configs importing the shipped SysApprovalRequest: the pin (record_change flow, start condition record.viewer.can_act == true) exits 1 with unknown field viewer on sys_approval_request at flow 'viewer_gate' · node 'start' (start) condition; control A, the object alone, exits 0 with ✓ Validation passed and 0 lines naming viewer; control B, action approval_approve rewritten to can_actt, exits 1 with unknown field viewer.can_actt (the read attachment viewer declares can_act, can_override, is_submitter) — did you mean viewer.can_act?. ABLATION, all legs on committed HEAD through scripts/ablation-replace.mjs, each landing proven on disk (anchor x1 to x0, replacement x0 to x1, blob 0832140517 to a new blob) and each restore proven (blob == HEAD 0832140517, git diff HEAD empty). Leg 1, unit: check reads the block at every site (the servedRowSite gate dropped), so validate-expressions.attached-on-read.test.ts gives 10 failed / 6 passed of 16. All 8 stored-row sites that check serves went red, plus the did-you-mean pin and the one-object-two-sites pin. The 5 served-row tests and the formula pin stayed green, as predicted: the formula judge is a separate call. Leg 2, unit: judgeFieldFormula reads the block, so 1 failed (the formula pin) / 15 passed. The unit subject is imported by relative path, so no dist is in that path and no dist preflight applies to it. Leg 3, CLI through dist: the leg-1 mutation, then pnpm --filter @objectstack/lint build, which emitted the JS bundles while its DTS step failed with TS6133 on the now-unused servedRowSite (an artifact of the mutation). ablation-dist-preflight.mjs @objectstack/lint 'objectName ? attachedOnReadIndex.get(objectName)' then found the marker in 4 built files (index.js, index.cjs, runtime.js, runtime.cjs), exit 0, and the pin config gave exit 0 with ✓ Validation passed: the card's original reading. Restore leg: trap-armed restore with absolute paths, blob == HEAD, lint rebuilt (exit 0), preflight --absent found the marker in none of 20 built files and the tree clean (exit 0), and the pin config gave exit 1 again. Expected direction (red) observed on every leg. ESLint, narrowed: eslint --no-inline-config --format json on the 3 changed .ts files: 3 files, 0 errors, 0 warnings. The population was read from eslint's own --print-config, which gives a config for the 3 .ts files and none for object.json or the .md, so the 3 are the whole linted population. Invariance: the effective parserOptions are { ecmaVersion: 'latest', sourceType: 'module' } with no project or projectService, so type-aware linting is off and this diff cannot change the verdict on any untouched file. The repository-wide pnpm lint is CI's.",
    "mcp_calls": "0 — none (all GitHub reads went through gh api REST GETs; no MCP GitHub tool loaded or called)",
    "api_writes": "3 relay strokes, each one session-side POST /repos/objectstack-ai/objectstack/dispatches executed by the fleet-write workflow as objectstack-fleet[bot]: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft, base main), which opened #22509; the relay read-back was 10682 bytes sent, 10682 stored, identical. (2) label-write --issue 22509 --assign zhuangjianguo, i.e. POST /repos//issues/22509/assignees; no label was written, and the read-back matched (assignee zhuangjianguo; the labels documentation, size/m, tests and tooling come from the labelers). (3) post-stamped --comment=22481, i.e. POST /repos//issues/22481/comments, this report. Not REST: 3 git pushes to claude/issue-22481-attached-block-served-rows (the empty-branch probe, ce338a7, 26088de), no force.",
    "open_questions": [
    {
    "question": "Field visibleWhen is the one site the measurement could not classify. It is render-only (fail-open) and evaluated over whatever row the surface holds: a served row on the console's approvals detail view, a stored row on a generic edit form. Should it join SERVED_ROW_SITES?",
    "options": [
    "A: keep it fields-only, as this PR ships it: the ruling's served list is the action predicates, and the default absorbs a site whose binding depends on the surface",
    "B: add 'field visibleWhen' to SERVED_ROW_SITES, so a block read there is accepted and its leaves are judged"
    ],
    "recommendation": "A. Business need: measured 0 field visibleWhen reading a block anywhere in the tree; the only shipped readers are sys_approval_request's 8 action predicates, so there is no pull. Long-term soundness: the list grows by binding evidence, one entry, when a producer appears. Preventing AI authoring errors: under B an AI writing record.viewer in a field visibleWhen passes os validate, yet on any surface holding a stored row the predicate faults and the render is fail-open, so the field is silently shown; A refuses it loudly at authoring. Startup focus: no widening without a named consumer. All four axes agree."
    },
    {
    "question": "This change makes two unreleased spec texts too broad. The .describe() of ObjectSchema.attachedOnRead (packages/spec/src/data/object.zod.ts about :2252) says record.BLOCK resolves with no site named, and the AttachedOnReadSchema docblock (about :1731) says lint's field index adds every declared block. Both are outside this claim's file surface, so they are not edited. Where should the fix go?",
    "options": [
    "A: widen this claim's file surface and carry both in a patch round on #22509: edit the describe and the docblock, run gen:schema and gen:docs (the json-schema shard and the 3 reference pages that render it), add an @objectstack/spec patch changeset",
    "B: a separate domain:spec card targeted at the same release"
    ],
    "recommendation": "A. Business need: the text ships in the json-schema and the reference docs AI authors read, in the same release as this narrowing. Long-term soundness: the contract text and its one reader land together, with no window where they disagree. Preventing AI authoring errors: a describe that says record.BLOCK resolves invites the flow-condition read this PR refuses; the refusal is loud, but it is a refusal the contract text set up. Startup focus: this is not new scope, only the one sentence of contract that states this reader. It costs about +5 changed lines plus generated artifacts, far under the 3000-line threshold."
    }
    ],
    "out_of_scope_findings": [
    "class: b · reach: exception: release-text (unreleased; the 22386 spec changeset is pending in pre.json, so the next release fixes this text into the json-schema and the reference docs) · evidence: packages/spec/src/data/object.zod.ts .describe() of attachedOnRead says 'Its reader is the shared build validator ... record.BLOCK resolves, and record.BLOCK.LEAF resolves only to a leaf the block declares', and the AttachedOnReadSchema docblock says '@objectstack/lint's field index adds every declared block name to the names record.FIELD may resolve to'; since 26088de lint refuses record.BLOCK at every site except action visible and disabled · Seam: spec:ObjectSchema.attachedOnRead (.describe) → runtime:packages/lint/src/validate-expressions.ts#SERVED_ROW_SITES · dedupe words: attachedOnRead describe served-row; AttachedOnReadSchema docblock field index every block; attachedOnRead spec prose record.BLOCK resolves · carrier: #22509 on a patch round if the seat widens the claim's file surface (open_questions[1]), else a domain:spec card",
    "carrier: PR #22494 (#22428, open draft, edits that docblock) · noted, not filed: the ExprSchemaHint.attachedOnRead doc comment in packages/formula/src/validate.ts says 'a caller lists each block name there too (@objectstack/lint's field index does)', which now holds at served-row sites only; it is a doc comment on an exported interface, so it ships in formula's d.ts",
    "carrier: this PR's changeset · noted, not filed: the unreleased .changeset/22386-validator-attached-on-read-leaf.md says the field index adds each block to the names a record member resolves to, at every site; .changeset/22481-attached-block-served-rows.md states the narrowing and refers to that earlier entry, so the compiled release notes carry both in order",
    "carrier: none · noted in Acceptance notes only: the MCP expression tool (packages/mcp) builds its record field set from fields only, so it refuses record.viewer in an action predicate that the build accepts. This divergence predates this card and is already recorded in the liveness note."
    ],
    "gates": {
    "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at HEAD 26088de (the last commit; stderr: 'gate list derived from the tree of objectstack-ai/objectstack at commit 26088de', change set 5 paths vs merge base e148ca9): 69 commands",
    "results": "68 exit 0; 1 exit 3: pnpm check:dual-build-cjs-loads, PREREQUISITE NOT MET (reads every package's dist/, and 9 packages unrelated to this diff are not built in this worktree: studio, client-react, connector-slack, embedder-openai, knowledge-memory, knowledge-ragflow, organizations, service-cluster-redis, +1), so NOT MEASURED locally and left to CI. Each exit code was captured before any pipe (each command's output redirected to its own log, then EXIT=$? read on the next statement), and the per-command record carries 'CMD :: exit N'.",
    "exit_0": "check-adr-0087-registration --base origin/main and --self-test; check-changeset-no-major --base origin/main and --self-test; check-ci-filter-parity; check-closing-keyword-parity and --self-test; check-comment-mask-adoption and --self-test; check-comment-mask-corpus; check-dev-prereqs --self-test; check-dts-emitted --self-test; check-empty-changeset --base origin/main and --self-test; check-issue-citations; check-keyed-text-bounds and --self-test; check-platform-object-tenancy-census and --self-test; check-plugin-teardown-shape and --self-test; check-registry-log-declared and --self-test; check-rest-log-spy-declared and --self-test; check-system-context-census and --self-test; check-undeclared-dep-imports and --self-test; docs-audit/check-affected-docs; docs-audit/check-drift-comment; pm/release-rehearsal-clone --self-test; release-pending-publish --self-test; spec check:duration-unit-keys, check:empty-state, check:liveness, check:strictness-ledger, check:variant-docs; check:changeset-gate-self-tests, check:cross-package-test-inputs, check:doc-authoring, check:docs-transcript-drift, check:driver-memory-census, check:dts-closure, check:engine-double-contract, check:gitlink-declared, check:issue-citations, check:lean-entry-closure, check:logger-receiver-detach, check:merge-driver, check:nul-bytes, check:objectql-double-limit, check:objectui-changeset, check:org-identifier, check:page-declaration-shape, check:platform-checklist, check:pm-changeset-deadline-census, check:published-files, check:query-options-erasure, check:refd-timer-probe, check:slot-lookup, check:sourcemap-no-sources-content, check:test-source-alias, check:tier-file-adoption, check:type-check-coverage, check:type-check-debt, check:watch-hint-literal, check:where-matcher",
    "ran_verdict": "✓ dispatch-gates --ran: 69 derived famil(ies) accounted for — 68 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3).",
    "outside_the_derivation": "NOT MEASURED, reason: CI-owned and named by the derivation as outside its 69: the 6 workflow-valued families, the 5 path-scheduled CI jobs (Test Core, Temporal Conformance, Dogfood Regression Gate, Dogfood Verify CLI, Build Core), the 4 CI type-check lanes, and the 51 artifact-roster families. The package-level type check this card owes, pnpm --filter @objectstack/lint typecheck, ran green (see tests)."
    },
    "line_budget": "PR #22509: +272 / -58 = 330 changed lines across 5 files, no generated files (git diff --numstat e148ca9..26088de, equal to the PR's own additions 272 and deletions 58). Under the 3000-line human-merge threshold. Per file: validate-expressions.ts +89/-22, validate-expressions.attached-on-read.test.ts +155/-28, authoring-rules.ts +10/-5, liveness/object.json +3/-3, .changeset/22481-attached-block-served-rows.md +15/-0.",
    "deviations": [
    "packages/spec/liveness/object.json: I also rewrote the attachedOnRead row's evidence field, besides the named producer and note. Its parenthetical said buildFieldIndex adds each declared block name to the names record.FIELD resolves to, which this change made false. It now cites validate-expressions.ts#SERVED_ROW_SITES (the anchor resolves; check:liveness 894 of 894). Same file and same row as the claim's surface.",
    "The CLI-level ablation (leg 3, through dist with ablation-dist-preflight) and a second unit leg for the formula judge were added beyond the card's single ablation. One mutation of the check closure cannot reach judgeFieldFormula, a separate call, so 'every surface' needed both legs to read red.",
    "Not edited, though this change makes them too broad: the spec .describe() and the AttachedOnReadSchema docblock in packages/spec/src/data/object.zod.ts. They are outside the claim's file surface, and editing them adds a verification surface (gen:schema, gen:docs, check:generated, a spec changeset). Raised as open_questions[1] and out_of_scope_findings[0].",
    "pnpm check:dual-build-cjs-loads is NOT MEASURED locally (PREREQUISITE NOT MET, whole-tree dist). I did not build the 9 unrelated packages it needs; CI measures it.",
    "Environment, not a choice: the first pnpm install in the worktree failed in better-sqlite3's node-gyp step ('gyp_main.py: Permission denied' under pnpm's temporary tool directory 10.31.0_tmp_7041). An immediate retry succeeded (exit 0) with no change made.",
    "Attribution follows AGENTS.md over the harness reminder, per the agent definition's precedence clause. Commits end with the model-free pair (Claude-Session URL plus Co-authored-by: Claude), and the PR body ends with the session-URL footer form, not the harness's model-named trailer and emoji footer."
    ],
    "files_changed": [
    "packages/lint/src/validate-expressions.ts",
    "packages/lint/src/validate-expressions.attached-on-read.test.ts",
    "packages/lint/src/authoring-rules.ts",
    "packages/spec/liveness/object.json",
    ".changeset/22481-attached-block-served-rows.md"
    ]
    }

  4. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat review of PR #22509 at 26088deb75: the build is right, and one patch round carries the spec text this change makes false. Then the contract review, then landing

    domain:spec seat 3 (#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-09T16:20Z · holder of claim 6083249520. Report: os-dev-report on this card. Thread-read: the report comment.

    Checked in the diff, not taken from the report (5 files, +272 / −58):

    • SERVED_ROW_SITES = ['action visible', 'action disabled'] is the one enumerated list.
    • The prose sites match the claim: the authoring-rules.ts pass-4 comment, and the liveness row's producer and note. The evidence rewrite is the report's declared deviation, in the same row, and this seat accepts it. The conformance test the note now cites is on main.
    • The changeset is @objectstack/lint patch, Clause-②: no. The acceptance it narrows is unreleased: .changeset/pre.json lists 0 consumed changesets.
    • The PR body's first line is Fixes #22481, and no closing keyword sits beside another card number.

    Evidence, as reported, with its shape checked:

    • @objectstack/lint 130 files / 5,954 tests; the spec repo project 54 / 915; check:liveness 894 / 894.
    • Through the built CLI, the card's flow config exits 1 with unknown field `viewer` , and both controls hold.
    • Three ablation legs on the committed head, the dist leg proven by ablation-dist-preflight, each read red, each restore blob-equal to HEAD.
    • --ran 69 / 69, with check:dual-build-cjs-loads NOT MEASURED (a prerequisite not met) and left to CI.
    • CI at 26088deb75: 13 success, 3 skipped, 16 in progress, 0 failures at this stamp.

    The open questions, answered by the seat:

    1. Field visibleWhen: A, it stays fields-only. The ruling's served-row subject is the action predicates, and the report measured 0 field visibleWhen reading a block. The list grows by binding evidence when a producer appears. All four axes agree.
    2. The spec text: A, carried in this PR. The .describe() of ObjectSchema.attachedOnRead and the AttachedOnReadSchema docblock in packages/spec/src/data/object.zod.ts say record.BLOCK resolves at every site. This change makes that false, and both ship in the same release (the spec ships src/**/*.zod.ts, and the describe reaches the JSON schema and the reference pages). A false statement this round creates is fixed in this round.

    Patch round:

    • Claim 6083249520's file surface is widened to packages/spec/src/data/object.zod.ts (that describe and that docblock only).
    • The spec artifacts are regenerated by their own generators. Build first, then check:generated and --fix only for what it proves stale; content/docs/references/** is merge=os-regen.
    • @objectstack/spec takes a patch entry.
    • The path limb (packages/spec/src/**) now holds, and the contract review at CONTRACT_REVIEW_TIER was owed already.

    Carried, not filed:


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 22481,
    "status": "done",
    "branch": "claude/issue-22481-attached-block-served-rows",
    "pr": "#22509",
    "head": "64277b3f28 (git rev-parse --short HEAD after the last commit; the remote branch and the PR head read 64277b3)",
    "session": "session_01KNKBCRDJCu5tGy3TEbvtrF — mode:subagent, patch round 1 under the PM; claim 6083249520 (newest Claim on #22481, Branch: claude/issue-22481-attached-block-served-rows, re-checked before the first edit), seat review 6084846568 read first",
    "premise_still_valid": true,
    "summary": "Patch round per the seat review 6084846568, both open questions answered A. (1) Field visibleWhen: unchanged, still fields-only. (2) The spec text is carried here: in packages/spec/src/data/object.zod.ts, the .describe() of ObjectSchema.attachedOnRead and the AttachedOnReadSchema docblock ('Its reader' paragraph) now state the served-row reading. A declared block resolves, and its leaves are judged, only in an action's visible and disabled predicates (SERVED_ROW_SITES in @objectstack/lint); every stored-row site (flow conditions, validation rules, field rule slots and formulas, option visibleWhen, sharing rules, hooks) refuses record.BLOCK as an unknown field. The new text carries no tracker number. Sequence: os-regen-merge.sh merged origin/main 446c8b2 as 90e3494, which brings PR #22494's formula change and a new lint test file; then the spec edit plus the @objectstack/spec patch entry in .changeset/22481-attached-block-served-rows.md, whose Clause-② no line is kept (588abcb); then spec build and check:generated, which proved 1 of 15 stale (check:docs); check:generated --fix regenerated only the three reference pages, committed alone (64277b3). No code change this round.",
    "tests": "All on HEAD 64277b3, heavy steps through os-verify-lock.sh (slot issue-22481), each exit captured before any pipe. Merge: bash scripts/pm/os-regen-merge.sh exit 0 (step 2 took main's side of the generated artifacts main moved; step 3 committed the merge before any regeneration). Regeneration: pnpm --filter @objectstack/spec build && check:generated gave '✗ 1 of 15 artifact(s) stale: content/docs/references/**' (CHECK_GENERATED_EXIT=1); pnpm --filter @objectstack/spec check:generated --fix gave VERDICT command-exit 0 and '--fix: regenerating 1 of the 1 stale artifact(s)'. Result: 3 pages, +4/-4, each differing from origin/main only on the attachedOnRead row. Then one locked script (VERDICT command-exit 0, all steps EXIT=0): (a) pnpm --filter '@objectstack/lint^...' build; (b) @objectstack/spec tests for object.zod.ts (src/data/object.test.ts, object-attached-on-read, object-image-field, object-strictness-batch20, src/system/metadata-form-zod-reconciliation, compose-stacks-collection-pipe-arm, compose-stacks-merge-collection-refusal): 7 files, 433 passed; (c) spec --project repo: 54 files, 915 passed; (d) @objectstack/lint typecheck: tsc --noEmit clean, check:test-typecheck OK; (e) @objectstack/lint vitest (the merge touched packages/lint): 131 files, 5985 passed, 5 skipped. The 5 skips are skipIf on an absent lint dist (lazy-deps.test.ts, runtime-lazy-deps.test.ts); after pnpm --filter @objectstack/lint build those two files gave 2 files, 10 passed, VERDICT 0. check:liveness: exit 0, 894 of 894 symbol anchors. Not re-run this round, because the order did not list them and no code changed: the plugin-approvals tests and the built-CLI pin (round 1 at 26088de). CI at read time: 64277b3 gave 33 success and 2 skipped of 35 check-runs; 588abcb, the head pushed before the regeneration, has TypeScript Type Check and 'Type Check · source gates' failed (cause not read; the expected one is the stale reference pages, which 64277b3 regenerates).",
    "mcp_calls": "0 — none",
    "api_writes": "1 REST write this round: post-stamped --comment=22481, i.e. a relay POST /repos/objectstack-ai/objectstack/dispatches executing POST /repos//issues/22481/comments (this report). No PR body PATCH (the delta is below, for the seat), no label or assignee write. Not REST: 2 git pushes to claude/issue-22481-attached-block-served-rows (90e3494 and 588abcb together, then 64277b3), no force. Reads only: gh api GETs, and the three PR-context roster gates' GitHub reads.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: the seat's note to #22428 · noted, not filed: the ExprSchemaHint.attachedOnRead doc comment in packages/formula/src/validate.ts still says '(@objectstack/lint's field index does)'. PR #22494 landed without changing it, and it now holds at served-row sites only.",
    "carrier: none · noted in the PR's Acceptance notes only: the MCP expression tool's fields-only record set predates this card and is recorded in the liveness note."
    ],
    "gates": {
    "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths, at HEAD 64277b3. Stderr: 'gate list derived from the tree of objectstack-ai/objectstack at commit 64277b3'; change set 9 paths vs merge base 446c8b2; 375 changed lines. It derived 110 commands, up from 69 in round 1: the packages/spec/src and content/docs paths added the spec and docs families.",
    "results": "106 exit 0, 4 exit 3 (NOT MEASURED). (1) pnpm --filter @objectstack/spec run check:skill-examples: PREREQUISITE NOT MET, packages/client-react/dist has no .d.ts. (2) pnpm check:dual-build-cjs-loads: PREREQUISITE NOT MET, every package's dist. (3) pnpm check:lean-entry-closure: PREREQUISITE NOT MET, packages/objectql/dist/core.*. (4) pnpm check:type-check-debt: its own turbo spec build was killed with exit 137 on the shared box, so nothing was measured. Exit 0 among the spec families: check:generated ('All 15 generated artifacts are up to date'), check:docs, check:api-surface, check:authorable-surface, check:liveness (894/894), check:strictness-ledger, check:variant-docs, check:yaml-examples, check:exported-any, check:dual-source-exports, check:entry-nameability, check:export-origins, check:llms-txt, check:skill-refs, check:browser-reachable-entries, check:objectui-pin-citations, check-spec-docblock-symbol-anchors and its --self-test; also check:doc-authoring, check:issue-citations and check:nul-bytes. The per-command record is in 'CMD :: exit N' form.",
    "ran_verdict": "✓ dispatch-gates --ran: 110 derived famil(ies) accounted for — 106 run, 4 NOT-MEASURED (4 DERIVED from a recorded exit 3).",
    "artifact_rosters": [
    "node scripts/check-changeset-fixed.mjs :: exit 0",
    "node scripts/check-closing-target-claim.mjs :: exit 2 (NOT WIRED: no PR context) — re-run with PR_NUMBER=22509 PR_HEAD_REF GITHUB_REPOSITORY GITHUB_TOKEN: exit 0",
    "node scripts/check-partof-closing-keyword.mjs :: exit 2 (NOT WIRED: no PR context) — re-run with PR_BODY = the stored #22509 body: exit 0",
    "node scripts/check-platform-checklist-watchdog.mjs :: exit 0",
    "node scripts/check-published-list-mirrors.mjs :: exit 0",
    "node scripts/check-sdui-manifest.mjs :: exit 0",
    "node scripts/check-single-claim-paths.mjs :: exit 2 (NOT WIRED: no PR context) — re-run with PR_NUMBER=22509: exit 0",
    "node scripts/check-skills-token-ratchet.mjs :: exit 0",
    "pnpm --filter @objectstack/spec run check:error-code-provenance :: exit 0",
    "pnpm --filter @objectstack/spec run check:meta-url-spelling :: exit 0",
    "pnpm --filter @objectstack/spec run check:react-blocks :: exit 0",
    "pnpm --filter @objectstack/spec run check:spec-changes :: exit 0",
    "pnpm check:auth-mount-ledger :: exit 0",
    "pnpm check:authz-resolver :: exit 0",
    "pnpm check:cli-examples-parity :: exit 0",
    "pnpm check:console-injection :: exit 0",
    "pnpm check:docs-image-tag :: exit 0",
    "pnpm check:engine-double-contract :: exit 0",
    "pnpm check:error-code-casing :: exit 0",
    "pnpm check:error-status-conformance :: exit 0",
    "pnpm check:filter-alias-parity :: exit 0",
    "pnpm check:future-spec-major :: exit 0",
    "pnpm check:i18n-stale-fill :: exit 0",
    "pnpm check:lockstep-package-count :: exit 0",
    "pnpm check:object-def-param-keys :: exit 0",
    "pnpm check:overlay-whitelist-table :: exit 0",
    "pnpm check:pm-governed-prose :: exit 0",
    "pnpm check:pm-label-desc-cap :: exit 0",
    "pnpm check:pm-settings-deny-roster :: exit 0",
    "pnpm check:published-readme-exports :: exit 3 (PREREQUISITE NOT MET: needs every package's built .d.ts; NOT MEASURED)",
    "pnpm check:route-ledger-census :: exit 0",
    "pnpm check:scaffold-emission-policy :: exit 0",
    "pnpm check:select-gate-families :: exit 0",
    "pnpm check:select-shard-packages :: exit 0",
    "pnpm check:skill-top-level-keys :: exit 0",
    "pnpm check:stack-collection-maps :: exit 0",
    "pnpm check:tenant-chokepoint :: exit 0",
    "node scripts/check-dts-references.mjs --self-test :: exit 0",
    "node scripts/check-platform-checklist-watchdog.mjs --self-test :: exit 0",
    "node scripts/check-published-list-mirrors.mjs --self-test :: exit 0",
    "node scripts/check-sdui-manifest.mjs --self-test :: exit 0",
    "node scripts/check-skills-token-ratchet.mjs --self-test :: exit 0",
    "node scripts/ci/scheduled-full-run.mjs --self-test :: exit 0",
    "node scripts/ci/select-shard-timings-run.mjs --self-test :: exit 0",
    "node scripts/pr-labels.mjs --self-test :: exit 0",
    "node scripts/release-verify-npm.mjs --self-test :: exit 0",
    "pnpm check:closing-target-claim :: exit 0",
    "pnpm check:commit-card-trailers :: exit 0",
    "pnpm check:partof-closing-keyword :: exit 0",
    "pnpm check:pm-write-pace :: exit 0",
    "pnpm check:single-claim-paths :: exit 0"
    ],
    "artifact_rosters_tally": "51 commands from the 'Artifact rosters' block of the no-path derivation. As first run, 47 exit 0, 3 exit 2 (NOT WIRED, no PR context) and 1 exit 3. The 3 NOT WIRED were re-run with PR context and all exit 0: check-closing-target-claim ('PR #22509 closes #22481, and each carries a Claim: whose Branch: line names claude/issue-22481-attached-block-served-rows'), check-partof-closing-keyword and check-single-claim-paths. That makes 50 exit 0 and 1 NOT MEASURED (check:published-readme-exports, which needs every package's built .d.ts). check:error-code-provenance, the family a sibling went red on: exit 0, 'OK — every registered-code stamp site is listed under its own owner key or carries a recorded waiver' (335 sites: 316 listed, 19 waived).",
    "outside_the_derivation": "NOT MEASURED, CI-owned: the workflow-valued families, the path-scheduled CI jobs, the CI type-check lanes (including pnpm --filter @objectstack/spec exec tsc --noEmit) and the 11 declared wide-population families."
    },
    "line_budget": "PR #22509 at 64277b3: +300 / -75 = 375 changed lines across 9 files against the merge base 446c8b2. This equals the PR's own additions 300, deletions 75 and changed_files 9. Generated files: content/docs/references/{data/object,api/metadata,system/migration}.mdx, +4/-4. Hand-written: object.zod.ts +22/-13, the changeset +17/-0, plus round 1's unchanged five files (validate-expressions.ts +89/-22, the attached-on-read test +155/-28, authoring-rules.ts +10/-5, liveness/object.json +3/-3). Under the 3000-line human-merge threshold.",
    "pr_body_delta": {
    "base": "the stored #22509 body, read back byte-identical to what was sent (10650 bytes without the trailing newline)",
    "how": "Apply in order. Each 'find' occurs exactly once (verified locally against that body). replace = substitute 'with' for 'find'; insert_before = put 'with' directly before 'find'; delete = remove 'find'. The result is 13297 bytes: first line unchanged ('Fixes #22481'), no closing keyword beside another card number, no angle brackets.",
    "ops": [
    {
    "op": "replace",
    "find": "- No new rule, no new refusal code, no export or signature change. @objectstack/formula is untouched. The pins use the dot spelling only, so the member-spelling change in PR #22494 does not move them.",
    "with": "- No new rule, no new refusal code, no export or signature change. @objectstack/formula is untouched. The pins use the dot spelling only, so the member-spelling change of PR #22494, now on main and merged into this branch at 90e34944, does not move them. The lint suite was re-run on the merged head.\n- @objectstack/spec: the .describe() of ObjectSchema.attachedOnRead and the AttachedOnReadSchema docblock (packages/spec/src/data/object.zod.ts) now state the served-row reading. A declared block resolves, and its leaves are judged, only in an action's visible and disabled; every stored-row site refuses record.BLOCK as an unknown field. check:generated --fix proved only check:docs stale and regenerated the three reference pages that render the describe. The changeset gains an @objectstack/spec patch entry."
    },
    {
    "op": "replace",
    "find": "All readings below are at 26088deb unless a line says otherwise.",
    "with": "All readings in this section are at 26088deb, the first round. The patch round's readings at 64277b3f are in their own section below."
    },
    {
    "op": "insert_before",
    "find": "## Acceptance notes\n",
    "with": "## Patch round: the spec text, at 64277b3f\n\n- Merge. bash scripts/pm/os-regen-merge.sh merged origin/main at 446c8b2a as 90e34944. Before the merge the branch held no generated artifact, so no os-regen path had two sides. Then came the spec edit (588abcb4) and the regeneration as its own commit (64277b3f).\n- Regeneration. pnpm --filter @objectstack/spec build, then check:generated: 1 of 15 artifacts stale (check:docs, content/docs/references/**). check:generated --fix regenerated only that one. The three pages differ from main only on the attachedOnRead row.\n- Tests.\n - The @objectstack/spec tests for object.zod.ts (object, object-attached-on-read, object-image-field, object-strictness-batch20, metadata-form-zod-reconciliation and the two compose-stacks pins): 7 files, 433 passed.\n - Spec --project repo: 54 files, 915 passed.\n - @objectstack/lint typecheck: clean, and check:test-typecheck OK.\n - @objectstack/lint vitest: 131 files, 5985 passed, 5 skipped. The 5 are skipIf on an unbuilt lint dist/ (lazy-deps, runtime-lazy-deps). After pnpm --filter @objectstack/lint build those 2 files gave 10 passed.\n- Spec gates. check:liveness exit 0, with 894 of 894 symbol anchors resolved. check:generated: all 15 up to date. check:docs, check:api-surface, check:authorable-surface and check-spec-docblock-symbol-anchors: exit 0 each.\n- Gates. dispatch-gates --commands at 64277b3f derived 110 commands: 106 exit 0, and 4 exit 3, which are NOT MEASURED.\n - check:dual-build-cjs-loads, check:lean-entry-closure and spec check:skill-examples need dist/ of packages this diff does not touch, and those are not built.\n - check:type-check-debt: the spec build it starts itself was killed (exit 137) on the shared box.\n - --ran: ✓ dispatch-gates --ran: 110 derived famil(ies) accounted for — 106 run, 4 NOT-MEASURED (4 DERIVED from a recorded exit 3).\n- Artifact rosters. The no-path derivation lists 51 commands. 47 exit 0 as run. 3 printed NOT WIRED without PR context, and with PR_NUMBER=22509 and this body all 3 exit 0: check-closing-target-claim, check-partof-closing-keyword, check-single-claim-paths. check:published-readme-exports exits 3, NOT MEASURED: it needs every package's built .d.ts. check:error-code-provenance exits 0 (335 stamp sites: 316 listed, 19 waived, every waiver live).\n\n"
    },
    {
    "op": "delete",
    "find": "- Spec text this change makes too broad (outside this card's file surface, not edited). This text is not released yet: the @objectstack/spec changeset that adds it is not consumed in .changeset/pre.json.\n - The .describe() of ObjectSchema.attachedOnRead (packages/spec/src/data/object.zod.ts, about :2252) says record.BLOCK resolves, with no site named.\n - The AttachedOnReadSchema docblock (about :1731) says lint's field index adds every declared block name.\n - Both should name the served-row sites. Editing the .describe() regenerates the JSON schema and the three reference pages that render it.\n"
    },
    {
    "op": "replace",
    "find": "That now holds at served-row sites only. PR #22494 is editing that docblock.",
    "with": "That now holds at served-row sites only. PR #22494 landed without changing that sentence; the seat carries a note to #22428."
    },
    {
    "op": "replace",
    "find": "5 files, +272 / -58 = 330 changed lines (generated files: none). Below the 3,000-line human-merge threshold.",
    "with": "9 files, +300 / -75 = 375 changed lines against the merge base 446c8b2a. Of those, the generated files are the three reference pages, +4 / -4. Below the 3,000-line human-merge threshold."
    }
    ]
    },
    "deviations": [
    "Sequence: I merged origin/main first (os-regen-merge.sh, merge commit 90e3494) and edited the spec text on the merged tree, so the regeneration ran on the merged sources and outside any MERGE state. The order allowed this ('merge once before pushing').",
    "I pushed 588abcb (the spec edit) before the regeneration, as a WIP backup before the lock-gated build. CI on that head failed TypeScript Type Check and 'Type Check · source gates'. 64277b3, the regeneration commit, followed and reads 33 success, 2 skipped. I did not read the cause of the 588abcb failure.",
    "In the AttachedOnReadSchema docblock I also replaced the stale clause 'the first package to declare a block settles them (#22387)' in the same 'Its reader' paragraph. That package has landed; the new clause says the flow resolver's columns-only set is this reading's own verdict. The clause is in the widened surface (the docblock). The docblock's header tag and its last paragraph, which carry older tracker numbers, are untouched.",
    "check:type-check-debt (derived, line 107) started its own turbo build of @objectstack/spec, which was killed with exit 137 and left this worktree's spec dist without index.d.ts. Ran after that: the 3 later derived gates (vendor-version-stamps, watch-hint-literal, where-matcher: source scans) and the 51 roster commands. I checked which of those read built output. Only check:published-readme-exports does, and it refused (exit 3). The spec rosters run from source (error-code-provenance skips dist; meta-url-spelling, spec-changes and react-blocks run their builders in --check mode). Every spec dist reader in the derived list (check:generated, check:docs, check:api-surface and the rest) ran before line 107, on the intact dist built at 17:08.",
    "Declared narrowing: 5 families NOT MEASURED locally. They are skill-examples, dual-build-cjs-loads and lean-entry-closure (dist of packages this diff does not touch), type-check-debt (killed build) and the roster check:published-readme-exports. To measure the first, second-to-last and third, I queued pnpm turbo run build for spec, client-react and objectql under os-verify-lock (slot issue-22481) about 8 times over about 80 minutes; it was never acquired. Holders: pid 4426, #22371's verify3.sh, alive and running cli unit tests, held 40 min or more; then pid 31913, #22450's batchC.sh. CI runs all five.",
    "The 3 NOT WIRED roster gates were judged against the PR body as stored now. After the seat applies pr_body_delta, check-partof-closing-keyword reads a new body. The delta adds no closing keyword and leaves the first line as is, but that run is not a reading of the new body."
    ],
    "files_changed": [
    "packages/spec/src/data/object.zod.ts",
    ".changeset/22481-attached-block-served-rows.md",
    "content/docs/references/data/object.mdx",
    "content/docs/references/api/metadata.mdx",
    "content/docs/references/system/migration.mdx",
    "packages/lint/src/validate-expressions.ts (round 1, unchanged)",
    "packages/lint/src/validate-expressions.attached-on-read.test.ts (round 1, unchanged)",
    "packages/lint/src/authoring-rules.ts (round 1, unchanged)",
    "packages/spec/liveness/object.json (round 1, unchanged)"
    ]
    }

  6. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT (patch round): PR #22509 at 64277b3f28. The contract review at CONTRACT_REVIEW_TIER is owed before it lands

    domain:spec seat 3 (#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-09T19:23Z · holder of claim 6083249520. Report: os-dev-report 6087692896, answering review 6084846568. Thread-read: 6087692896.

    Checked in the diff and on the head, not taken from the report:

    • The PR's file list is 9 files: round 1's five, plus packages/spec/src/data/object.zod.ts, the changeset and the three regenerated reference pages.
    • The describe and the docblock now state the served-row reading. A block resolves only in an action's visible / disabled (SERVED_ROW_SITES), and every stored-row site refuses record.<block> as an unknown field. The new text carries no tracker number.
    • The docblock's stale clause "the first package to declare a block settles them" is replaced, in the same paragraph. That is the report's declared deviation, inside the widened surface, and it is accepted.
    • The changeset is @objectstack/lint patch and @objectstack/spec patch, Clause-②: no. A describe correction changes no accept or reject result.
    • The regeneration is its own commit (64277b3f): only check:docs was stale, and three pages each move +1 / -1.
    • The size: 375 changed lines, under the 3,000-line threshold.
    • The PR body: the seat applied the report's six edits, each matching exactly once, and the relay read the body back identical. The first line is still Fixes #22481.

    CI at 64277b3f28: 33 success, 2 skipped, 0 failures; it has settled. The earlier 588abcb4 red was the pre-regeneration head, which this head supersedes.

    Declared NOT MEASURED, left to CI: dual-build-cjs-loads, lean-entry-closure, spec check:skill-examples, type-check-debt (its own build was killed with exit 137 on the shared box), and the roster check:published-readme-exports.

    Carried: the ExprSchemaHint.attachedOnRead doc sentence in packages/formula/src/validate.ts still names lint's field index without the site qualifier. PR #22494 landed without that edit. The seat's note on #22428 (6084861204) stands, and the formula lane may take it.

    Next: the PR stays a draft, marked needs:contract-review, until a same-head PASS is on record. Then it lands through the queue.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #22509 → c76edeb8c6 (Fixes #22481). The card is closed as completed

    domain:spec seat 3 (#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-09T20:03Z · holder of claim 6083249520.

    • Landed: through the merge queue at 2026-10-09T20:02Z as c76edeb8c6. Its one parent is ee8751d41e, and it is an ancestor of origin/main. The queue did not eject it.
    • Content check against the reviewed head 64277b3f28:
      • 6 of the 9 PR paths on c76edeb8c6 are blob-equal to that head.
      • The other 3 also moved on main after the branch's last merge (446c8b2a6 → ee8751d41e): validate-expressions.ts and two reference pages. For each, the head-to-merge differences are exactly main's own changes, the same line sets.
      • Every line the PR added to validate-expressions.ts is present on the merge.
    • The review chain: ACCEPT 6084846568 (round 1) and 6087742700 (patch round); contract review PASS 6087890118 at 64277b3f28, confirmed by the seat against the PR's record.
    • What now holds:
      • A declared attachedOnRead block resolves, and its leaves are judged, only at an action's visible / disabled (SERVED_ROW_SITES).
      • Flow conditions, validation rules, field rules and formulas, option visibleWhen, sharing rules and hooks refuse record.BLOCK as an unknown field at os validate / os build and the object save door.
      • The spec describe and docblock, and the liveness row, state the same reading.
    • Carried: the ExprSchemaHint.attachedOnRead doc sentence in packages/formula/src/validate.ts (note 6084861204 on formula: the unknown-field check reads only the dot spelling record.FIELD, so record['typo'] and previous['typo'] pass os build and the object save door at every record-scoped slot #22428).

    This act removes pm:dispatched; the domain, type, priority, area and target: labels stay.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:workflowApprovals and automation — the work that runs without a person driving itbugSomething isn't workingdomain:specpriority:p3target:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions