Uh oh!
There was an error while loading. Please reload this page.
feat(lint): canonical-envelope page audit gets a shared home; cloud-connection joins the gated population - #11574
Conversation
…ck/lint; gate cloud-connection's pages The three-door detector from platform-objects' canonical-expression-envelopes gate moves to @objectstack/lint beside page-walk.ts as auditPageExpressionEnvelopes / renderBareExpressionFindings, so every package shipping raw-literal Page exports can run the same gate. platform-objects' gate now consumes the shared export (population scan, preconditions, verdict and shipped-page downgrade control unchanged); cloud-connection gains a thin gate over its two shipped pages, with @objectstack/lint devDependency and the anchored vitest source alias. MarketplaceInstalledPage is declared : Page (type-level only) so export-shape discovery sees it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T9cDbY2NBiVJWYx3BpWfH2
📓 Docs Drift CheckThis PR changes 2 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 7 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 3a4592fc903543308c7f5e934446c412d23c38f1 && git checkout 3a4592fc903543308c7f5e934446c412d23c38f1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7bdb1564a0166f46ff7ac006ca05273daf1cbda6 1291491a361db408df9be1a837b1009cf26f730d && git checkout -B drift-repro 7bdb1564a0166f46ff7ac006ca05273daf1cbda6 && git merge --no-ff 1291491a361db408df9be1a837b1009cf26f730d
node scripts/docs-audit/affected-docs.mjs --json 7bdb1564a0166f46ff7ac006ca05273daf1cbda6
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#11480
Clause-② — this PR stays DRAFT, held for the PM's contract review (it widens
@objectstack/lint's published API). Do not flip ready or queue it; the PM lands it.Why a shared home (Option A, per the PM claim on the card)
The three-door canonical-envelope detector landed package-local in
platform-objects(PR #11481, for issue #11255) because apackages/spectest reading platform-objects sources tripscheck:cross-package-test-inputs— so it could not reach the raw-literalPagea sibling published package ships (packages/cloud-connection/src/cloud-connection-ui.ts:19). Copying the detector per package is the failure modepage-walk.ts's own header records as having already produced one dead rule. So the detector moves to@objectstack/lintbeside the walk it is built on, and each owning package runs a thin population test over its ownPageexports. Route B (a second copy) re-creates the documented dead-rule hazard; route C leaves the card's stated hazard live (the next predicate ships bare, silently).Exported API (
packages/lint/src/page-envelope-audit.ts)auditPageExpressionEnvelopes(page, pageLabel)— runs the three parse doors (PageSchema/PageComponentSchema/ComponentPropsMap[type]) over one authored page and unions their findings; returnsPageEnvelopeAuditcarryingfindingsplus the four door-precondition channels (pageParseError,componentParseErrors,unmappedTypes,unreadableProps) andcomponentCount, so a door that cannot open is reported rather than reading as clean.renderBareExpressionFindings(findings)— the actionable red an author reads in CI (page, path, authored source, door, fix prescription). Exported because every consuming gate asserts on exactly this rendering.BareExpressionFinding,PageEnvelopeAudit,EnvelopeAuditDoor.collectBare(the single-door lockstep walk) is exported at module level only, NOT from the barrel — it exists sopage-envelope-audit.test.tscan prove door necessity by running one door in isolation; consumers always want the three-door union.The module header carries the full TSDoc: the hazard (a raw-literal page is type-checked, never parsed, so bare predicates reach the wire verbatim and objectui's shape-router sends them to the legacy fail-soft evaluator), the door table with each door's blind spot, the behavioural identification rule (a position is flagged only when the author wrote a string and the parse turned that same string into an envelope — no key names hardcoded), and the precondition-reporting contract. The detector's own unit tests (the five fixture-based negative controls / door-necessity proofs from the platform-objects file) moved with it and run once, at its home.
@objectstack/lintkeeps no api-surface / export-origins-style generated registry (checked:packages/lint/scripts/holds only the two doc-check gates; the barrel pinrule-id-barrel-exports.test.tscovers rule-id constants, which this module does not add) — nothing to regenerate.platform-objects: refactored, not weakened
canonical-expression-envelopes.test.tsnow imports the detector from@objectstack/lint(already source-aliased in its vitest config). Everything the landed gate asserted survives: the export-shape population scan oversrc/with the barrel-coverage assert and the ≥3 floor, all four per-page door-precondition tests, the real-page findings assertion, and the shipped-page downgrade control (SysUserDetailPage, deep-cloned, envelope downgraded to bare → red namingslots.alerts[0].visibleWhen). That control is also the reverse-verification surface: ablate the lint module and this file reds. All 22 original test intents survive across the two packages (17 here, 5 at the detector's home).cloud-connection: joins the population
@objectstack/lintdevDependency + newvitest.config.tswith the anchored array-form source alias (mirrors platform-objects; anchored because@objectstack/lintships a second./runtimesubpath — the prefix-matching object form would mangle it to ENOTDIR).check:test-source-aliasgreen; the package's ledger entry is unchanged (the new test's only workspace value import is the aliased lint).src/canonical-expression-envelopes.test.ts: same export-shape population scan pointed at this package'ssrc/, bidirectional (a declared page nobody audits reds, and an audited page the scan cannot see reds — forcing the: Pageannotation convention), floor ≥2; the four door preconditions; the verdict; and downgrade controls injecting a bare predicate into deep clones of BOTH shipped pages (red names page +regions[1].components[0].visibleWhen).MarketplaceInstalledPage(src/marketplace-ui.ts), served throughMARKETPLACE_INSTALLED_UI_BUNDLE.pages, invisible to export-shape discovery because it was never annotated: Page. Bounded in-place fix, same defect class as the card: the declaration is now annotated: Page(type-level only, zero runtime change; siblingCloudConnectionSettingsPagein the same package is the evidence for the shape). Verified: the DTS build reads the annotation (bogus-key injection reds with TS2353 againstPage; restored byte-identical), and the one downstream consumer of the bundles (packages/cli/test/platform-page-i18n-parity.test.ts) reads them with optional chaining + any-cast and stays green.cloud-connection:panelandmarketplace:installed-listare console-registered widget types with noComponentPropsMaprow. The test asserts the unmapped set EQUALS the exemption list per page, and additionally asserts every exempted component authors an EMPTY props bag — the moment a real key lands there, the gate forces the decision (props schema row, or a conscious wider exemption). Both pages author zero expression keys today (measured), so the gate lands green over a real population.The qa/downstream-contract exclusion from #11255 stands unchanged.
Verification (all runs at commit
1291491a36, the PR head; every exit captured before any pipe; heavy commands serialized throughscripts/pm/os-verify-lock.sh)pnpm --filter @objectstack/lint test: 81 files / 2263 passed;typecheckexit 0. New detector suite: 5/5.pnpm --filter @objectstack/platform-objects test: 28 files / 447 passed;typecheckexit 0. Gate file verbose run: 17/17.pnpm --filter @objectstack/cloud-connection test: 26 files / 283 passed (25 pre-existing + the new gate; gate file verbose run: 15/15). Package rebuilds green incl. DTS.packages/lint/src/page-envelope-audit.ts(marker grep = 1 on disk, original spelling = 0; both consuming suites resolve lint through the vitest SOURCE alias, so the mutation leg needs no rebuild and dist is not on any of these resolution paths). Result: lint detector suite 3 failed, platform-objects gate 1 failed (the downgrade control), cloud-connection gate 2 failed — failure text names page + key path (regions[1].components[0].visibleWhen). Restore proven byte-identical (git hash-object= committed blob1bf3b779…); all suites re-green.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, derived at1291491a36): all path-derived and convention-triggered gates run and green —check:cross-package-test-inputs,check:test-source-alias,check:type-source-resolution,check:published-files,check:slot-lookup,check:changeset-gate-self-tests,check:objectui-changeset,check:override-consistency,check:query-options-erasure,check:engine-double-contract,check:where-matcher,check:i18n(after building the CLI closure it requires),check:type-check-coverage,check:type-check-debt --re-measure(32 entries, none above ceiling),check:nul-bytes, adr-0087-registration, changeset-fixed, changeset-no-major, ci-filter-parity, empty-changeset, osv-exemptions, plugin-teardown-shape, affected-docs. Each verdict read from the gate's own OK/failure line, not a piped$?.pnpm --filter '...@objectstack/cloud-connection' --filter '...@objectstack/lint' typecheck— exit 0 across the sweep; plus the CLI's bundle-consuming parity test 5/5.Changeset
One changeset:
@objectstack/lintminor (new public API),@objectstack/cloud-connectionpatch (type-level annotation). platform-objects' diff is tests-only and needs no entry. Not declared-breaking, so no ADR-0087 marker is owed (check:adr-0087-registrationgreen).Generated by Claude Code