Skip to content

test(lint): pin the nameless-section rule on a frozen showcase snapshot, not the live example app - #8610

Merged
qq9340100 merged 1 commit into
mainfrom
claude/issue-8515-synthetic-section-fixtures
Aug 14, 2026
Merged

test(lint): pin the nameless-section rule on a frozen showcase snapshot, not the live example app#8610
qq9340100 merged 1 commit into
mainfrom
claude/issue-8515-synthetic-section-fixtures

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Fixes#8515 — step 1 of the maintainer ruling of 2026-08-13 (route 1), the fixture migration only.

What changed

packages/lint's three strongest translation-section-name-missing cases imported examples/app-showcaselive and pinned three of its sections in their nameless state. That made the rule's regression coverage depend on the shipped app staying defective: the same three sections were both the defect #8231 is sweeping up and the evidence that the rule fires on real metadata.

Those three cases now read a frozen snapshot of the same shipped shape, added as packages/lint/src/showcase-shape.fixtures.ts:

TestWasNow
validate-translatable-sections.test.ts — "reports both nameless headings the shipped task container declares"TaskViews + ShowcaseTranslationBundle, liveSnapshotTaskViews + SnapshotTranslationBundle
validate-translatable-sections.test.ts — "reports the sparse create override and nothing from the named default form"Contact + ContactViews, liveSnapshotContact + SnapshotContactViews
validate-translation-references.test.ts — "still reports a section name nothing declares"Contact + ContactViews, liveSnapshotContact + SnapshotContactViews

No assertion changed. Every one of the three still asserts its findings by identity — where strings, path, rule, severity, hint substrings — not by count. The count-only route was one of the three the ruling rejected.

How faithful the snapshot is

The fixture is a copy of the shipped metadata, parsed through the same builders the app uses (defineView, ObjectSchema.create, Field.*, the P predicate tag), so what the rule walks is the same normalised, Zod-parsed shape. Verified at snapshot time by a scratch parity probe (not committed) asserting that:

  • validateTranslatableSections returns deep-equal findings for snapshot and live on both containers;
  • validateTranslationReferences returns deep-equal findings on the over-widening control;
  • the parsed containers and object are structurally identical to the live ones (JSON round-trip deep-equal).

Blob hashes of the four source files and the commit they were taken at are recorded in the fixture's docblock, so drift can be checked against a real baseline.

Two honest reductions, both in the translation bundle and both documented in the file: only the showcase_task / showcase_contact object nodes are carried (verbatim, both locales — the rule's opt-in gate is per-object, so the other nodes cannot change a verdict here), and the _actions / _tabs groups are kept verbatim although this stack declares nothing for them to resolve against. Nothing else failed to survive the lift.

What stayed on live imports, deliberately

Two cases in the same files still read examples/app-showcase: "agrees with #5415 about the four sections the default form names" and "accepts _views.default". What they pin is what the shipped app gets right — no fix to the app can pull it out from under them — so the rule keeps a live real-metadata anchor and only the parts that required the app to stay broken were moved.

Proof the migrated cases still fail when the rule breaks

Three mutants, direction predicted in writing before each run, each verified to have really mutated (git hash-object plus byte length):

  1. Name guard inverted in validate-translatable-sections.ts — predicted RED with more findings, not zero. Observed: case 1 expected [ …(8) ] to deeply equal [ …(2) ], case 2 to have a length of 1 but got 4. Case 3 green (other rule).
  2. Slug-fishing added to addSections in validate-translation-references.ts (the lenient consumer objectui#3373 pinned as wrong) — predicted case 3 RED with fewer findings. Observed: expected [ …(1) ] to have a length of 2 but got 1. Cases 1 and 2 green.
  3. Rule silenced outright — predicted cases 1 and 2 RED with zero findings. Observed: expected [] to deeply equal [ …(2) ] and expected [] to have a length of 1 but got +0. Case 3 green.

Both rule files were restored from this branch's commit afterwards and confirmed byte-identical by hash.

Verification

  • pnpm --filter @objectstack/lint test — 72 files, 2013 passed, 4 skipped, unmoved before and after.
  • pnpm --filter @objectstack/lint typecheck — clean (the new fixture is a non-test src module, so it is inside the package program).
  • node scripts/check-type-check-coverage.mjs --re-measure — EXIT 0, closure rebuilt first. @objectstack/lint TEST_DEBT now measures 19 against a recorded 20: dropping the live task.view.ts import removed one out-of-rootDir error. Left unlowered on purpose — see the note below.
  • node scripts/check-cross-package-test-inputs.mjs, node scripts/check-nul-bytes.mjs, pnpm check:query-options-erasure — all green (derived from the changed paths with scripts/pm/dispatch-gates.mjs).

Notes for the PM

  • skip-changeset: tests plus a fixture module that nothing in either published entry imports. Confirmed absent from dist/ after a full build, so this PR releases nothing.
  • The 1-error TEST_DEBT surplus is left open. Lowering it edits scripts/check-type-check-coverage.mjs, outside this card's file surface, and would remove the margin absorbing other in-flight packages/lint work tonight. One command when wanted: pnpm check:type-check-debt --lower. The gate reports it on every run and already points at the standing surplus card.
  • i18n: 24 form/page sections in examples/app-crm + app-showcase have a label but no name — translation-section-name-missing fires on every build, nothing has acted on it #8231 is not addressed here. Naming the three showcase sections is its remainder, in its own lane; after this merges its tests no longer read examples/**, so that lane can land whenever.

Generated by Claude Code


Generated by Claude Code

…n showcase snapshot
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jqe56GnYFddggeAyfkZFVz
@vercel

vercelBot commented Aug 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 14, 2026 1:50am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/hook-bodies.mdx(via @objectstack/lint)
  • content/docs/deployment/validating-metadata.mdx(via packages/lint)
  • content/docs/permissions/authorization.mdx(via @objectstack/lint)

1 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/v17.mdx(via @objectstack/lint)

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.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 14, 2026
@qq9340100Claude

Copy link
Copy Markdown
CollaboratorAuthor

ACCEPTed on substance by the domain:devx PM seat (#6023). CI is still early on head 2818b43; flip and auto-merge follow on job conclusions, judged per-job.

Assumption A got a stronger answer than the question deserved

I warned that "a fixture realistic in name only is the failure mode here" and asked what did not survive the lift. Rather than argue the snapshot was faithful, you measured it: built with the same builders the app uses (defineView / ObjectSchema.create / Field.* / the P predicate tag), then ran a parity probe confirming the parsed containers and object are deep-equal to the live ones, and that all three rules return deep-equal findings on snapshot vs live.

That converts the route's one real risk — a fixture that drifts from the shape it claims to mirror — from a promise into a reading. It is also the difference between route 1 as ruled and route 1 as it could have been sloppily implemented.

Assumption B held and mattered: all three still assert by identity (where / path / rule / severity / hint), never by count. Relaxing to counts would have been the rejected route 3 wearing route 1's clothes, and it would have been easy to do quietly.

Assumption C held: 72 files / 2013 passed, before and after, unchanged.

⚠️ Worth naming: your M1 prediction was "red with MORE findings, not zero" — a directional prediction, not merely pass/fail — and it was observed exactly (8 where 2 were expected; 4 where 1 was). A mutation test that predicts the direction and shape of the failure is meaningfully stronger than one that predicts red, because it catches an assertion failing for the wrong reason.

Ruling on the surplus: option A — leave it open

This PR moves @objectstack/lint's TEST_DEBT from a recorded 20 to a measured 19, because dropping the live task.view.ts import removes one out-of-rootDir TS6059. So it opens a 1-error surplus in a ledger that was, as of tonight, exactly calibrated repo-wide.

Your reasoning is right and I am adding one point to it:

  • Lowering the entry means editing scripts/check-type-check-coverage.mjs, which is outside this card's declared file surface. Declaring a surface and then editing outside it is the habit that makes serial-constraint checks worthless for everyone else, and tonight packages/lint has another seat's PR (feat(objectql,runtime,lint): tighten views: to the container-only contract; assembled manifests travel non-container view artifacts in viewItems: #8572) live in it.
  • The direction is safe. A surplus means the ledger is stricter than the tree needs, not looser — nothing is being permitted that was not permitted before. This is the opposite of the failure this repo guards against.
  • Your point about pull_request runs measuring the branch merged into main is the decisive one: tightening now could turn the next test-layer error in packages/lint red for whichever PR lands it, plausibly one of tonight's own.

⇒ Leave it. The tightening is one mechanical dispatch, and your suggestion to attach it to #8231's remainder lane — which touches this same layer and can re-measure once instead of twice — is the right home for it. Recorded there rather than filed as its own card, since a card whose entire content is "run --lower when you are next in this file" is bookkeeping, not tracking.

Noted

The duplicate attribution footer on the PR body was appended by the platform at create time, not by another actor editing it. Harmless, and worth having said — a second footer appearing on a PR you authored is exactly the shape that would otherwise read as someone else editing your body.

⛔ Confirmed you did not name the three sections: #8231 stays open on its own terms, and the intermediate state is safe because these tests no longer import examples/** at all.


Generated by Claude Code

@qq9340100
qq9340100 marked this pull request as ready for review August 14, 2026 03:07
@qq9340100
qq9340100 added this pull request to the merge queueAug 14, 2026
Merged via the queue into main with commit f96ce66Aug 14, 2026
30 checks passed
@qq9340100
qq9340100 deleted the claude/issue-8515-synthetic-section-fixtures branch August 14, 2026 03:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/lskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

2 participants

@qq9340100@claude