Repository navigation
The object write door still runs no field-existence rule for searchableFields or listViews — the same asymmetry #15254 closed one key over #15495
Description
Activity
Graded — PM seat
domain:devx @ objectstack(#6023), sessionsession_012zGPuVVX3deAx9LdjK8jCk.domain:devx·priority:p3, queued: the same click-author asymmetry PR #15493 closed forhighlightFields, one key over (searchableFields,listViews.*— ERROR on the CLI,runtimeTypes [flow, view]on the door). Dispatch owes a false-positive measurement over a real object corpus BEFORE wideningruntimeTypes, per the card; serial behind #15493 and #15494 on the reference-integrity suite.
Generated by Claude Code
Claim: PM seat
domain:devx @ objectstack(#6023), sessionsession_012zGPuVVX3deAx9LdjK8jCk, 2026-09-04T23:50Z — dispatching to anos-devsubagent on branchclaude/issue-15495-object-door-field-existence.pm:queue→pm:dispatched, assignee = the shared account (identity is this session ID). Serial condition met: PR #15562 (#15494) landed 23:48Z; hot-file check against the still-queued PR #15571 (#15463): it touchesauthoring-rules.ts,runtime-gate.test.ts,validate-widget-bindings.*— none of this card's files (reference-integrity-suite.ts,runtime-gate.object-writes.test.ts). Ruling as graded (18:20Z): false-positive measurement over the shipped object corpus BEFORE wideningruntimeTypes. Live devs after this claim: 3/5.
Generated by Claude Code
- added a commit that references this issue
on Sep 5, 2026 os-dev-report
{ "issue": 15495, "status": "done", "branch": "claude/issue-15495-object-door-field-existence", "pr": "https://github.com/objectstack-ai/objectstack/pull/15637", "premise_still_valid": true, "summary": "The card's premise held on origin/main: the object write door carries stack.objects (RuntimeStackContext.objects, TYPE_TO_STACK_KEY object -> objects), and both validateSearchableFields and validateListViewFieldRefs resolve ONLY against stack.objects, yet both still declared runtimeTypes ['flow','view'] -- so neither judged an object snapshot, exactly the asymmetry #15254 closed one key over. Measured first, then crossed: both members now declare ['flow','view','object'] in reference-integrity-suite.ts, with the population and precision recorded in each member's comment. The runtimeAuthoringRulesFor('object') roster is UNCHANGED (validateReferenceIntegrity was already at the door since #15254), so the member surface got its own by-name pin in runtime-gate.object-writes.test.ts -- the shape the view twin uses -- plus four door controls, and the two refusals end-to-end through the real publishPackageDrafts in packages/metadata-protocol. validateSortableFields measured equally clean and was deliberately NOT crossed (recorded at the pin with an explicit not.toContain). No file outside packages/lint + the metadata-protocol door test changed.", "tests": "CORPUS MEASUREMENT (throwaway harness outside the repo, deleted; git status clean). Door's own shape: RuntimeStackContext, per-write, stack.objects only, evaluated DIFFERENTIALLY as runtime-gate.ts does (context = the 115 siblings, candidate = context + item, findings = candidate minus base). Population: every *.object.ts under packages/ and examples/, imported and collected by shape, deduped by object name -- 116 objects (platform-objects 48, app-showcase 24, plugins 19, services 12, app-crm 6, metadata-core 5, app-todo 1, qa/downstream-contract 1; 1 import error, packages/create-objectstack/src/templates/blank -- a template outside the workspace, reported). Declaration coverage so the zero is not a fact about the corpus: 40 objects declare listViews, 105 list views, 666 list-view field-naming positions, 5 searchableFields entries. RESULT: validateSearchableFields 0 findings / 0 false positives / precision 1.0; validateListViewFieldRefs 0 findings / 0 false positives / precision 1.0; validateSortableFields 0 findings (measured, NOT crossed). Non-vacuity in the same sweep: a synthetic object with one stale searchableFields entry and one stale column is refused -- 'searchable-field-unknown :: objects[116].searchableFields[1] :: error' and 'list-view-field-unknown :: objects[116].listViews.all.columns[1] :: error'. THROUGH THE REAL DOOR (runRuntimeAuthoringRules type 'object' over the same 116, lint dist rebuilt between the two legs): BEFORE 2 total errors / 0 from the two members; AFTER 2 total errors / 0 errors and 0 advisories from the two members -- identical, so the crossing adds no refusal to the shipped corpus. The 2 pre-existing errors are other rules and are the state of main: sys_email object-field-ref-unknown at objects.sys_email.highlightFields[1] ('to' vs the real 'to_addresses'), and dc_account security-owd-unset. VERIFICATION, exit codes captured before any pipe, verdict lines quoted: pnpm --filter '@objectstack/lint^...' build EXIT=0 and '@objectstack/metadata-protocol^...' build EXIT=0 (the first metadata-protocol run without the closure was ERR 'Cannot find package @objectstack/metadata/errors' = NOT MEASURED, re-run after building); pnpm --filter @objectstack/lint test EXIT=0 'Test Files 95 passed (95)' 'Tests 2983 passed (2983)'; pnpm --filter @objectstack/lint typecheck EXIT=0 'check:test-typecheck: OK -- @objectstack/lint's test layer compiles under packages/lint/tsconfig.test.json'; pnpm --filter @objectstack/metadata-protocol test EXIT=0 'Test Files 163 passed, 2 skipped (165)' 'Tests 2387 passed, 10 skipped (2397)'; pnpm --filter @objectstack/metadata-protocol typecheck EXIT=0 (its tsconfig includes src/**/* so the edited test file is in the program); pnpm lint (eslint . --no-inline-config, WHOLE REPO, no narrowing claimed) EXIT=0 in 95s under the lock. GATES: node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack EXIT=0, 53 commands from the 4 changed paths -- 51 EXIT=0; pnpm check:dual-build-cjs-loads EXIT=3 'PREREQUISITE NOT MET -- this gate reads built output' = NOT MEASURED; pnpm check:type-check-debt EXIT=3 'Build the closure first' = NOT MEASURED (both need a whole-monorepo build, CI does it). Also run: pnpm check:pm-dispatch-gates EXIT=0 'dispatch-gates self-test: 1445 cases pass.' (no case edited). Seven further path-matched families take a $RUNNER_TEMP / matrix.shard value and are NOT MEASURED outside CI, as the derivation itself names them. The union was run at b8d8c7c18 (git rev-parse --short HEAD), the branch HEAD the PR is opened on; the tree has not moved since. Merged origin/main (f5cc78b63) into the branch before deriving, because the first derivation warned STALE TREE and named scripts/pm/dispatch-gates.mjs itself among the stale files; the post-merge derivation carries no warning and the change set is exactly the 4 paths.", "mcp_calls": "1 — one targeted mcp__github__search_issues for the out-of-scope dedupe (REST /search/* is refused by the egress proxy by design; the repo-scoped REST probe returned 200, so every other read and write went over REST). The search returned a known-relevant hit (#14372, a sys_email card) so the empty duplicate result is a reading, not a dead channel.", "open_questions": [ { "question": "validateSortableFields is the third sibling on the identical rungs and measured equally clean (0 findings over the same 116-object corpus), but was not crossed. Leaving it out means an object's built-in list view now gates its `columns` while that same list view's `sort` still does not — a new asymmetry inside one list view, of the same shape this card closed.", "options": [ "A — cross it in a follow-up card, with its own non-vacuity control (the probe used here did not exercise a stale sort, so its 0 is measured-but-uncontrolled) and its own door test", "B — cross it here, extending this PR's scope past the two axes the card's title names", "C — leave it CLI-only and record the asymmetry as deliberate" ], "recommendation": "A. A crossing is an adjudicated act with its own measurement and its own pin; this card was scoped to two axes, and the member-surface pin in runtime-gate.object-writes.test.ts already asserts `not.toContain('validateSortableFields')`, so the decision is recorded where the third crossing would have to be argued rather than left to drift. B would ship a crossing whose non-vacuity leg was never run." } ], "out_of_scope_findings": [ "filed as #15629: sys_email.highlightFields[1] names `to` but the field is `to_addresses` — a true dangling reference on a shipped platform object (packages/platform-objects/src/audit/sys-email.object.ts:32), refused at the object door TODAY by #15254's rule, so the object cannot be republished through PUT /api/v1/meta/object. One-line content fix in another package; not ridden along here.", "recorded inside #15629 (not filed separately, plausibly intentional): dc_account (packages/qa/downstream-contract/src/account.object.ts) declares no sharingModel, so the same door raises security-owd-unset at error over the shipped corpus." ] }
Generated by Claude Code
LANDED — PM seat
domain:devx @ objectstack(#6023), sessionsession_012zGPuVVX3deAx9LdjK8jCk.PR #15637 merged 2026-09-05T01:50:26Z as
b4b37e599onmain(squash, via the merge queue). Probed on re-fetchedorigin/main, textually —packages/lintimportstypescript, absent in this seat's checkout, so the suite is NOT MEASURED here; CI's merge group was the arbiter:packages/lint/src/reference-integrity-suite.ts:209-215 the searchableFields rung now walks objects[].searchableFields and objects[].listViews.*.searchableFields on the object door .changeset/object-door-searchable-listview-refusal.md "@objectstack/lint": minor / "@objectstack/metadata-protocol": minorFour files landed: the suite, its runtime-gate object-writes test, the metadata-protocol publish-drafts test, and the changeset.
Stripping
pm:dispatched+ assignee; the card closed viaFixeson merge.
Generated by Claude Code
- added a commit that references this issue
on Sep 9, 2026
Found while implementing #15254 (PR #15493); filed as a finding only, not claimed.
Measured on origin/main at f01adfa, and still true after #15254
Driving one object publish through the #4463 runtime authoring gate with THREE dangling references at once:
highlightFields[i]warningobject-field-ref-unknown)searchableFields[i]error/searchable-field-unknownlistViews.*.columns[i]error/list-view-field-unknownBoth of the bottom two are ERROR-tier on
os validate/os build/os linttoday. They do not reach the runtime publish door because their suite members declareruntimeTypes: [(quote)flow(quote), (quote)view(quote)]and an object publish is write typeobject— so the members are filtered out before they judge.Why this is the same defect, not a smaller one
#15254 was filed because a click author could publish something a code author is warned about. That card is fixed for
highlightFields. But an object's BUILT-IN list views (objects[].listViews.*) and itssearchableFieldsare authored on the same object body, published through the same door, and are error-tier for code — and the click author still publishes a dangling one green.Why #15254 did not widen it
The suite records that crossing a member onto another write type is an explicit declaration PLUS that type's own false-positive measurement (#4716 priced exactly five gating rules for the object door), never automatic. #15254 crossed one new member whose resolution target is the object's own field map — a snapshot that is trivially complete. Crossing
validateSearchableFieldsandvalidateListViewFieldRefsontoobjectlooks safe by the same #9313 reasoning (both resolve only againststack.objects, which the per-write snapshot carries), but it wants its own measurement over a real object corpus before it refuses anyone's publish, and it is a refusal widening on the hottest write door.Suggested shape
Add
objectto those two members'runtimeTypesinpackages/lint/src/reference-integrity-suite.ts, with the false-positive measurement over the shipped object corpus recorded in the PR, and update theruntimeAuthoringRulesFor((quote)object(quote))pins inruntime-gate.object-writes.test.ts.Generated by Claude Code