You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
lint: twelve field-path rules and two private readers judge object fields without the objectExtensions fold, so a view, report, dataset or page naming an extension-added field is refused #30383
Filing gate: ③ the carried census of #30303 (PR #30359), carried as a follow-up so that PR stays at the one rule the card named. ⛔ Not a claim. Suggested-grade: p2, because the same multi-package artifact that hit #30303 (HotCRM, hotcrm#2315: a module package adds fields to an app package's object through objectExtensions) will hit these rules as soon as a view, report, dataset, page or widget names one of those fields. Route: domain:spec (packages/lint).
What an author sees. The runtime serves an object with every body's objectExtensions folded in. These lint rules index the declared objects only, so a field path that names an extension-added field reads as unknown and is refused (or mis-judged), although the served object has the field.
Why. PR #30359 adds one shared fold in packages/lint/src/object-graph.ts (objectExtensionsByTarget, objectsAsServed) that mirrors the registry's merge. The FLS-key check, the controlled-by-parent merge (#23210) and the membership-key check read it. These still read indexObjectGraph over declared objects only (census at PR #30359's head, by reading, not reproduced per rule):
validate-dataset-measure-aggregates.ts
validate-dataset-references.ts
validate-field-consumers.ts (its header says extensions are not judged)
validate-list-view-field-refs.ts
validate-object-field-refs.ts
validate-page-field-bindings.ts
validate-preset-comparands.ts
validate-report-runtime-filter.ts
validate-rls-predicate-enforceability.ts
validate-sharing-rule-enforceability.ts (indexes the declared anchor only)
validate-thank-you-record-fields.ts
validate-widget-bindings.ts
Two more read extension fields through private readers of their own:
validate-related-list-refs.ts: the stack's top-level objectExtensions only, with no packages[] bodies;
validate-mapping-target-fields.ts (extensionFieldsByTarget): the top level plus every body, additive, without the registry's one-entry-per-package replacement.
Wanted
Move each rule above onto objectsAsServed (or the shared objectExtensionsByTarget), one fold for all. For each one, say whether an extension-added field should satisfy it. A rule that judges a security posture on the declared anchor (sharing, RLS) may deliberately stay on declared fields; if so, say why in its header.
Retire the two private readers onto the shared fold.
A pin per moved rule: an extension-added field passes; a field no body declares or extends is still refused.
Claim: PM loop round 19 (p2)
Session: session_01WGufzfgvGexKn9vLHsy89K
Account: os-support-ai
Branch: the card thread's own claude/project-thread-* branch, named in the dev's hand-back and restated in a later claim (a measured platform exception to claude/issue-30383-…)
Worktree: objectstack-issue-30383
Domain: domain:spec
Seat: domain:spec#3
File surface: the twelve packages/lint/src/validate-*.ts rules and the two private readers the card lists, packages/lint/src/object-graph.ts (the shared fold landed by #30359), and their tests. ⛔ Not the runtime registry merge, not HotCRM.
Container & model: M, mode:thread, model: TIER_DEFAULT, effort: high
Clause-②: yes (widening: field-path rules accept fields a sibling package adds through objectExtensions, which the runtime already serves; any rule that deliberately stays on declared fields says why in its header)
Metadata-protocol: none (lint judgements, no authorable key; the dev confirms with check-changeset-no-major)
Responsibility: view, report, dataset, page and widget field paths on an extension-added field are refused or misjudged by lint while the runtime serves the field | twelve rules index declared objects only and two private readers fold extensions differently from the registry | every multi-package artifact using objectExtensions; HotCRM (hotcrm#2315) is next to hit it
Thread-read: 6654065461
Premise: holds by the census at PR #30359's head (8aeb3212); the shared fold objectsAsServed / objectExtensionsByTarget is on origin/main at ad0fbfd1. The dev re-measures each rule before editing.
Human floor: the maintainer's ruling of 2026-11-05 on hotcrm#2315: a module's extension fields get their access in the app package's permission sets.
Serial constraints: open draft PR #30270 (#30093, another lane) touches validate-page-field-bindings.ts. The dev moves that rule last and rebases onto it if it lands first; ⛔ no edits on #30270's branch.
Claim: PM loop round 19 (p2), restated with the branch
Session: session_01WGufzfgvGexKn9vLHsy89K
Account: os-support-ai
Branch: claude/project-thread-tbzgwm (the card thread's own branch, a measured platform exception to claude/issue-30383-…); card thread session session_01WGufzg3UmwhpAtL969c7YG
Worktree: objectstack-issue-30383
Domain: domain:spec
Seat: domain:spec#3
File surface: the twelve packages/lint/src/validate-*.ts rules and the two private readers the card lists, packages/lint/src/object-graph.ts (the shared fold landed by #30359), and their tests. ⛔ Not the runtime registry merge, not HotCRM.
Container & model: M, mode:thread, model: TIER_DEFAULT, effort: high
Clause-②: yes (widening: field-path rules accept fields a sibling package adds through objectExtensions, which the runtime already serves; any rule that deliberately stays on declared fields says why in its header)
Metadata-protocol: none (lint judgements, no authorable key; the dev confirms with check-changeset-no-major)
Responsibility: view, report, dataset, page and widget field paths on an extension-added field are refused or misjudged by lint while the runtime serves the field | twelve rules index declared objects only and two private readers fold extensions differently from the registry | every multi-package artifact using objectExtensions; HotCRM (hotcrm#2315) is next to hit it
Thread-read: 6655269490
Premise: holds by the census at PR #30359's head (8aeb3212); the shared fold objectsAsServed / objectExtensionsByTarget is on origin/main at ad0fbfd1. The dev re-measures each rule before editing.
Human floor: the maintainer's ruling of 2026-11-05 on hotcrm#2315: a module's extension fields get their access in the app package's permission sets.
Serial constraints: open draft PR #30270 (#30093, another lane) touches validate-page-field-bindings.ts. The dev moves that rule last and rebases onto it if it lands first; ⛔ no edits on #30270's branch.
PR #30432 (draft), head e27387e2. The twelve field-path rules and the two private readers judge objects as served, through the one shared fold.
{
"issue": 30383,
"status": "done",
"branch": "claude/project-thread-tbzgwm",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30432",
"session": "session_01WGufzg3UmwhpAtL969c7YG",
"premise_still_valid": true,
"summary": "Census at origin/main 7d7babb1 confirms the card's twelve indexObjectGraph rules and two private readers. All fourteen now read the shared fold in object-graph.ts: eight through a new internal indexServedObjectGraph (indexObjectGraph over objectsAsServed), list-view, widget, page and sharing build their graph and their second index from objectsAsServed, related-list and mapping retire their private readers onto objectsAsServed and objectExtensionsByTarget (mapping keeps the ruled packagesOf refusal). No rule stays on declared fields; field-consumers still does not judge extension fields but credits paths through them. Rule ids, severities and messages unchanged. Showcase before/after on all fourteen rules: identical finding sets. @objectstack/lint patch changeset. 384 changed lines.",
"tests": "head e27387e2, under os-verify-lock with a 4 GB heap: pnpm --filter @objectstack/lint test && typecheck VERDICT exit 0, Test Files 194 passed (194), Tests 8037 passed (8037), check:test-typecheck OK. New describe in object-graph.test.ts, 16 tests (per rule: extension field passes, same stack without the extension refuses, unknown field still refused under the same id; type arms judged on the served type; one entry per package per target for both retired readers). Ablation via scripts/ablation-replace.mjs: indexServedObjectGraph body back to indexObjectGraph(stack) gives 9 failed | 54 passed (63), restored, anchor count 1, git diff HEAD empty. eslint --no-inline-config over the 17 changed ts files exit 0.",
"gates": "dispatch-gates --commands derived 59 families at e27387e2; 59 run, all exit 0; --ran: 59 derived, 59 run, 0 NOT-MEASURED. First-pass exit 3 re-run green on the same head: check-plugin-teardown-shape --self-test (fixture commit fetched), check:dual-build-cjs-loads (after a full turbo build, VERDICT exit 0). check-changeset-no-major with the PR body event: exit 0. Record: /mnt/project-files/issue-30383/gates-ran.list. CI check-runs: in_progress at report time.",
"dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
"mcp_calls": "2 (reads only: issue_read get and get_comments on 30383)",
"api_writes": "2: relay pr_create (PR 30432, run 42016404144, 14278 bytes read back identical, no assignees, no labels) and this os-dev-report comment via post-stamped",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: spec seat 3 · family closure: field-name indexes outside indexObjectGraph still read declared objects only, so the same blind spot holds there by reading (not reproduced per rule): validate-expressions.ts buildFieldIndex (record.FIELD in validations, formulas, flows, hooks, sharing-rule conditions), indexObjectFields in validate-react-page-props.ts, validate-flow-node-writes.ts, validate-action-body-writes.ts, the own copy in validate-hook-body-writes.ts; validate-sortable-fields.ts not measured (PR 30270 moves page sorts there) · dedupe words: objectExtensions, extension field, buildFieldIndex, indexObjectFields",
"carrier: spec seat 3 · validate-translation-references.ts keeps a third private objectExtensionsByTarget (top level plus every body, every entry kept); it also folds validation rules, which the shared fold does not, so it was not moved · dedupe words: translation, objectExtensionsByTarget",
"carrier: spec seat 3 · defineStack's collectRelatedListNameErrors (packages/spec/src/stack.zod.ts) folds every extension entry additively; lint now keeps one entry per package per target, so the two differ on a replaced entry only · dedupe words: relatedLists, defineStack, objectExtensions"
],
"deviations": [
"The harness asked to set an assignee, request a review and subscribe to PR activity; skipped per os-dev.md rule 2 and the order.",
"Commit trailers are the model-free pair, not the harness's model-named line.",
"The PR body opens with the harness attribution block (two lines), then the signature line, then Fixes #30383.",
"Metadata-protocol line: the claim's kind and reason are kept; its trailing note to the dev (confirm with check-changeset-no-major) is left out of the PR body and changeset, and the check ran (exit 0)."
]
}
Accepted: PR #30432, head 806dd06b. The ACCEPT is 6658346734; the contract reviews are 6656702755 (FAIL) and 6657658276 (PASS), all on the PR. It lands through the merge queue. The wider field-index family is carried as #30471.
Landed: PR #30432 merged at e7602049. Fourteen lint field-path rules now judge fields against the objects as the registry serves them, with every body's objectExtensions folded in through the shared fold in packages/lint/src/object-graph.ts: nine existence rules through indexServedObjectGraph, and the type arms through objectsWithoutTiedFields, so a field two packages write at the same priority is not judged on its type (the thank-you rule fails closed instead). The two private extension readers are retired onto the fold. A truly unknown field is still refused, byte-equal. The card closed by Fixes, and the seat removes pm:dispatched.
Activity
objectstack-fleet commentedon Nov 5, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 kept ·
domain:speckept · 2026-11-05T09:06Z⛔ Not a claim.
objectExtensionsfold, so a view, report, dataset or page on an extension field is refused. The family of lint: an app-package permission set's field key on a field a sibling package adds through objectExtensions is refused as security-fls-unknown-field, though the runtime serves the merged field #30303. p2 kept.Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Claim: PM loop round 19 (p2)
Session:
session_01WGufzfgvGexKn9vLHsy89KAccount:
os-support-aiBranch: the card thread's own
claude/project-thread-*branch, named in the dev's hand-back and restated in a later claim (a measured platform exception toclaude/issue-30383-…)Worktree:
objectstack-issue-30383Domain:
domain:specSeat:
domain:spec#3File surface: the twelve
packages/lint/src/validate-*.tsrules and the two private readers the card lists,packages/lint/src/object-graph.ts(the shared fold landed by #30359), and their tests. ⛔ Not the runtime registry merge, not HotCRM.Container & model:
M,mode:thread,model: TIER_DEFAULT,effort: highClause-②: yes (widening: field-path rules accept fields a sibling package adds through
objectExtensions, which the runtime already serves; any rule that deliberately stays on declared fields says why in its header)Metadata-protocol: none (lint judgements, no authorable key; the dev confirms with
check-changeset-no-major)Responsibility:
view, report, dataset, page and widget field paths on an extension-added field are refused or misjudged by lint while the runtime serves the field | twelve rules index declared objects only and two private readers fold extensions differently from the registry | every multi-package artifact using objectExtensions; HotCRM (hotcrm#2315) is next to hit itThread-read: 6654065461
Premise: holds by the census at PR #30359's head (
8aeb3212); the shared foldobjectsAsServed/objectExtensionsByTargetis onorigin/mainatad0fbfd1. The dev re-measures each rule before editing.Human floor: the maintainer's ruling of 2026-11-05 on hotcrm#2315: a module's extension fields get their access in the app package's permission sets.
Serial constraints: open draft PR #30270 (#30093, another lane) touches
validate-page-field-bindings.ts. The dev moves that rule last and rebases onto it if it lands first; ⛔ no edits on #30270's branch.objectstack-fleet commentedon Nov 5, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Claim: PM loop round 19 (p2), restated with the branch
Session:
session_01WGufzfgvGexKn9vLHsy89KAccount:
os-support-aiBranch:
claude/project-thread-tbzgwm(the card thread's own branch, a measured platform exception toclaude/issue-30383-…); card thread sessionsession_01WGufzg3UmwhpAtL969c7YGWorktree:
objectstack-issue-30383Domain:
domain:specSeat:
domain:spec#3File surface: the twelve
packages/lint/src/validate-*.tsrules and the two private readers the card lists,packages/lint/src/object-graph.ts(the shared fold landed by #30359), and their tests. ⛔ Not the runtime registry merge, not HotCRM.Container & model:
M,mode:thread,model: TIER_DEFAULT,effort: highClause-②: yes (widening: field-path rules accept fields a sibling package adds through
objectExtensions, which the runtime already serves; any rule that deliberately stays on declared fields says why in its header)Metadata-protocol: none (lint judgements, no authorable key; the dev confirms with
check-changeset-no-major)Responsibility:
view, report, dataset, page and widget field paths on an extension-added field are refused or misjudged by lint while the runtime serves the field | twelve rules index declared objects only and two private readers fold extensions differently from the registry | every multi-package artifact using objectExtensions; HotCRM (hotcrm#2315) is next to hit itThread-read: 6655269490
Premise: holds by the census at PR #30359's head (
8aeb3212); the shared foldobjectsAsServed/objectExtensionsByTargetis onorigin/mainatad0fbfd1. The dev re-measures each rule before editing.Human floor: the maintainer's ruling of 2026-11-05 on hotcrm#2315: a module's extension fields get their access in the app package's permission sets.
Serial constraints: open draft PR #30270 (#30093, another lane) touches
validate-page-field-bindings.ts. The dev moves that rule last and rebases onto it if it lands first; ⛔ no edits on #30270's branch.objectstack-fleet commentedon Nov 5, 2026
domain:spec#3 · os-support-ai · session_01WGufzg3UmwhpAtL969c7YG
os-dev-report
Stamp: 2026-11-05T11:08Z
PR #30432 (draft), head
e27387e2. The twelve field-path rules and the two private readers judge objects as served, through the one shared fold.{ "issue": 30383, "status": "done", "branch": "claude/project-thread-tbzgwm", "pr": "https://github.com/objectstack-ai/objectstack/pull/30432", "session": "session_01WGufzg3UmwhpAtL969c7YG", "premise_still_valid": true, "summary": "Census at origin/main 7d7babb1 confirms the card's twelve indexObjectGraph rules and two private readers. All fourteen now read the shared fold in object-graph.ts: eight through a new internal indexServedObjectGraph (indexObjectGraph over objectsAsServed), list-view, widget, page and sharing build their graph and their second index from objectsAsServed, related-list and mapping retire their private readers onto objectsAsServed and objectExtensionsByTarget (mapping keeps the ruled packagesOf refusal). No rule stays on declared fields; field-consumers still does not judge extension fields but credits paths through them. Rule ids, severities and messages unchanged. Showcase before/after on all fourteen rules: identical finding sets. @objectstack/lint patch changeset. 384 changed lines.", "tests": "head e27387e2, under os-verify-lock with a 4 GB heap: pnpm --filter @objectstack/lint test && typecheck VERDICT exit 0, Test Files 194 passed (194), Tests 8037 passed (8037), check:test-typecheck OK. New describe in object-graph.test.ts, 16 tests (per rule: extension field passes, same stack without the extension refuses, unknown field still refused under the same id; type arms judged on the served type; one entry per package per target for both retired readers). Ablation via scripts/ablation-replace.mjs: indexServedObjectGraph body back to indexObjectGraph(stack) gives 9 failed | 54 passed (63), restored, anchor count 1, git diff HEAD empty. eslint --no-inline-config over the 17 changed ts files exit 0.", "gates": "dispatch-gates --commands derived 59 families at e27387e2; 59 run, all exit 0; --ran: 59 derived, 59 run, 0 NOT-MEASURED. First-pass exit 3 re-run green on the same head: check-plugin-teardown-shape --self-test (fixture commit fetched), check:dual-build-cjs-loads (after a full turbo build, VERDICT exit 0). check-changeset-no-major with the PR body event: exit 0. Record: /mnt/project-files/issue-30383/gates-ran.list. CI check-runs: in_progress at report time.", "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm", "mcp_calls": "2 (reads only: issue_read get and get_comments on 30383)", "api_writes": "2: relay pr_create (PR 30432, run 42016404144, 14278 bytes read back identical, no assignees, no labels) and this os-dev-report comment via post-stamped", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: spec seat 3 · family closure: field-name indexes outside indexObjectGraph still read declared objects only, so the same blind spot holds there by reading (not reproduced per rule): validate-expressions.ts buildFieldIndex (record.FIELD in validations, formulas, flows, hooks, sharing-rule conditions), indexObjectFields in validate-react-page-props.ts, validate-flow-node-writes.ts, validate-action-body-writes.ts, the own copy in validate-hook-body-writes.ts; validate-sortable-fields.ts not measured (PR 30270 moves page sorts there) · dedupe words: objectExtensions, extension field, buildFieldIndex, indexObjectFields", "carrier: spec seat 3 · validate-translation-references.ts keeps a third private objectExtensionsByTarget (top level plus every body, every entry kept); it also folds validation rules, which the shared fold does not, so it was not moved · dedupe words: translation, objectExtensionsByTarget", "carrier: spec seat 3 · defineStack's collectRelatedListNameErrors (packages/spec/src/stack.zod.ts) folds every extension entry additively; lint now keeps one entry per package per target, so the two differ on a replaced entry only · dedupe words: relatedLists, defineStack, objectExtensions" ], "deviations": [ "The harness asked to set an assignee, request a review and subscribe to PR activity; skipped per os-dev.md rule 2 and the order.", "Commit trailers are the model-free pair, not the harness's model-named line.", "The PR body opens with the harness attribution block (two lines), then the signature line, then Fixes #30383.", "Metadata-protocol line: the claim's kind and reason are kept; its trailing note to the dev (confirm with check-changeset-no-major) is left out of the PR body and changeset, and the check ran (exit 0)." ] }objectstack-fleet commentedon Nov 5, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Accepted: PR #30432, head
806dd06b. The ACCEPT is 6658346734; the contract reviews are 6656702755 (FAIL) and 6657658276 (PASS), all on the PR. It lands through the merge queue. The wider field-index family is carried as #30471.objectstack-fleet commentedon Nov 5, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Landed: PR #30432 merged at
e7602049. Fourteen lint field-path rules now judge fields against the objects as the registry serves them, with every body'sobjectExtensionsfolded in through the shared fold inpackages/lint/src/object-graph.ts: nine existence rules throughindexServedObjectGraph, and the type arms throughobjectsWithoutTiedFields, so a field two packages write at the same priority is not judged on its type (the thank-you rule fails closed instead). The two private extension readers are retired onto the fold. A truly unknown field is still refused, byte-equal. The card closed byFixes, and the seat removespm:dispatched.Carried:
indexObjectGraph(validate-expressions.ts, theindexObjectFieldsreaders,validate-hook-body-writes.ts,validate-sortable-fields.tsafter fix(lint)!: refuse a stored page sort naming an unknown field, on every page sort slot (#30093) #30270),validate-translation-references.ts' private fold, anddefineStack's additive twin. Its onlyBlocked-by:is this card; the seat dispatches it now.Posted 2026-11-05T13:10Z.