Skip to content

fix(lint): validateFormLayout resolves a list-bound view's object for its default form - #16417

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-16168-form-layout-list-binding
Sep 6, 2026
Merged

fix(lint): validateFormLayout resolves a list-bound view's object for its default form#16417
baozhoutao merged 1 commit into
mainfrom
claude/issue-16168-form-layout-list-binding

Conversation

@baozhoutao

@baozhoutao baozhoutao commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #16168

What

validateFormLayout now falls back to the container's list binding (list.data.object / list.object / list.objectName, via the shared viewObjectName) when resolving the bound object for a form site, whenever the container itself carries no object / objectName / data.object of its own. This is the same third rung validate-translatable-sections.ts:178-179 already carries for its own sites (recordObject / listBinding) — carried, not duplicated, into validate-form-layout.ts:132/142.

Before: a view container that binds its object only through the list block (HotCRM's own shape, all 14 of its views) had containerObject === undefined, so every site under it — including its own default form — could never resolve an object, and form-field-unknown / form-section-group-unknown never fired there, however wrong the section content was.

packages/lint/src/validate-form-layout.ts — the fix (2 new lines + 1 changed line + comments).
packages/lint/src/validate-form-layout.test.ts — new describe block ("falls back to the container list binding"): P1/P2 (red-first on unmodified code, confirmed), N1 positive control (byte-identical where/path with object also on the container), N2 clean list-bound container, N3 a formViews[] entry with its own binding (unchanged), N4 no binding anywhere stays silent (unchanged), plus a pin proving absolute-colspan-discouraged was never gated on the object binding (see "colspan" below).
.changeset/lint-form-layout-list-binding.md@objectstack/lint patch.

Why

Filed by the hotcrm epic's rule-survey seat (hotcrm card 1637): on hotcrm at db5fe702, 49 of 49 authored form sections across 14/14 views were unreachable by this validator because every one of those views binds its object only via list.data.object. Triaged (comment 5556877712) against origin/main: the diagnosis holds on the platform source, and the fix shape already exists one validator over — this PR carries that same ladder into validate-form-layout.ts, and only there (the package's other two ladder lengths are a separate convergence card per that triage, out of scope here).

Colspan — measured, not assumed

The filing seat's own scope correction (comment 5556746408) flagged absolute-colspan-discouraged as unmeasured on the "never fires" claim. Measured here: the colspan check (validate-form-layout.ts's (b) block) sits outside the known-gated block — it needs only entry.colSpan != null, never objName / known. So it was never actually dead on a list-bound container; it already fired regardless of whether the object resolved. This PR's fix therefore changes only form-field-unknown and form-section-group-unknown.

Risk & cost (rollback)

Advisory-only (severity: 'warning') — nothing is blocked. A stack whose list-bound container's default form (or an unbound formViews.* entry) references a real dangling field/group now surfaces a warning it did not surface before; a clean stack of the same shape produces no new output (N2/N4 pin this). Patch-level change to @objectstack/lint, single-file source diff, revert is a straight git revert.

Open questions for the maintainer

See report JSON open_questions (posted as a comment on this issue) — notably what the groups legacy-bucket alias (card 6926) now surfaces once this binding resolves, per the triage seat's ask; reported there for information, not acted on here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8

… its default form

Carries the list-binding fallback rung already used one validator over
(validate-translatable-sections.ts:178-185) into validate-form-layout.ts:
a container whose object lives only on `list.data.object` (HotCRM's own
shape, all 14 of its views) had no object of its own, so `containerObject`
resolved to undefined for every site under it — including its own default
`form` — and form-field-unknown / form-section-group-unknown never fired
there.

absolute-colspan-discouraged is unaffected: measured, that check sits
outside the known-gated block and was never gated on the object binding.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint, touching 1 documentable anchor(s).

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

  • content/docs/releases/v12.mdx (via validateFormLayout (symbol, a top-level function))
  • content/docs/releases/v17.mdx (via validateFormLayout (symbol, a top-level function))

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
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 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.

Coarse fallback — 5 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 dff0bdd3ac51df2653e1bd7beb7f2b3ca0fa58c3packageMentionDocs.

Which tree this was computed on

This run read content/docs from 326fa8d78447e1aef88c1473628f8742ca7856e9 — the merge of head 85f9543d3a7a1496100b46536006ca79930e9006 into base dff0bdd3ac51df2653e1bd7beb7f2b3ca0fa58c3, 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 326fa8d78447e1aef88c1473628f8742ca7856e9 && git checkout 326fa8d78447e1aef88c1473628f8742ca7856e9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dff0bdd3ac51df2653e1bd7beb7f2b3ca0fa58c3 85f9543d3a7a1496100b46536006ca79930e9006 && git checkout -B drift-repro dff0bdd3ac51df2653e1bd7beb7f2b3ca0fa58c3 && git merge --no-ff 85f9543d3a7a1496100b46536006ca79930e9006

node scripts/docs-audit/affected-docs.mjs --json dff0bdd3ac51df2653e1bd7beb7f2b3ca0fa58c3

⚠️ 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 dff0bdd3ac51df2653e1bd7beb7f2b3ca0fa58c3 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@claude

claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

CI red classified — not this PR's; one re-run taken — PM seat domain:devx @ objectstack (#6023), session session_01Vbw3RPgdtqesx4azk9SbW8, 2026-09-06T22:02Z.

At head 85f9543d3, Test Core (1/6) (run 34061451793, job 101562634411) failed on one test in a package this diff does not touch:

FAIL src/email-service.queue-delivery.test.ts > EmailService — queue delivery on > still refuses the queue for attachments OVER the limit, and stores nothing (#5177)
Error: Test timed out in 5000ms.
@objectstack/plugin-email:test  Tests 1 failed | 467 passed (468)

The diff is packages/lint/src/validate-form-layout.ts + its test + a changeset; @objectstack/plugin-email neither depends on @objectstack/lint nor exercises it. Shard 1 stopped at that failure, so @objectstack/spec, @objectstack/connector-openapi and @objectstack/client-react were scheduled but never reached (check-test-completeness: 5 of 8 reported, 3 never reached) — the shard is INCOMPLETE, not merely red on one test. Every other row at this head is green, shard 5 included.

Per landing-operations B: a failure naming a service the diff does not touch earns exactly one re-run to confirm it reproduces identically; rerun_failed_jobs on run 34061451793 requested at 22:02Z. If it reproduces on the same test, the failure is real and outside this PR — this seat files it as a finding for triage (a 5 s timeout on a queue-delivery test is a flake-shaped defect in its own right) and this PR waits on a green shard 1, ⛔ no test skipped, no second re-run. If the re-run is green, the flip proceeds. Next read 22:15Z.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 6, 2026 22:16
@baozhoutao
baozhoutao enabled auto-merge September 6, 2026 22:16
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 7ad2ca0 Sep 6, 2026
53 of 55 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-16168-form-layout-list-binding branch September 6, 2026 22:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants