Skip to content

Arming ADR-0080 at the runtime save gate needs two decisions, not one: where the manifest comes from, AND whether validateJsxPages may ever leave CLI_ONLY #13863

Closed

Description

@zhuangjianguo

Measured for #13445 (dev seat, this measurement re-derived on origin/main at f532630d02 — re-derive rather than quote, this repo moves fast)

#13445 asked for a measurement, not a fix: supply a manifest to the runtime save gate in a probe, run it against the runtime corpus, count first-run findings and their true/false ratio, then write that reading plus the three candidates' blast radii here. Both are done below. #13445 closes as a consequence of this card existing.

The reading: 0/0 at the real gate, 232/0%-real if the gate were widened — and those are different questions

At the real production mechanism (evaluateRuntimeAuthoringGate → runRuntimeAuthoringRules, called with a real sduiManifest argument, confirmed reachable from a probe with zero production edits): 0 findings, always, for any input — not because the runtime corpus is clean, but because validateJsxPages — the only rule in the shared registry that reads ctx.sduiManifest — carries surfaces: CLI_ONLY (packages/lint/src/authoring-rules.ts) and is therefore excluded from runtimeAuthoringRulesFor()'s output for every metadata type by construction (r.surfaces.includes('runtime-publish') is false for it, full stop). This is machine-pinned (authoring-rule-wiring.test.ts pins runtimeAuthoringRulesFor('view')'s exact member list, which excludes it) and independently reproduced live: a probe drove a deliberately-broken kind:'html' page + the real manifest through the actual runtime-gate call shape for type:'page' — rulesRun came back ["validatePresetComparands"] (a different, legitimately-runtime-wired rule), never validateJsxPages; errors/advisories carried zero jsx-* entries.

Positive control (proves the harness/manifest are not the reason for the zero): calling validateJsxPages directly, bypassing the surface filter, on the identical broken page + identical real manifest returns 9 findings across jsx-forbidden-tag / jsx-mismatched-tag / jsx-unclosed-element — the rule and manifest are both live and working; only the runtime-surface wiring keeps them apart.

A2.2, falsified in the strong form the dispatch order flagged as possible: "the runtime corpus" — defined as the checked-in kind:'html'/'jsx' page items anywhere in this repo that a live PUT/save could ever write — is not merely similar to the authoring corpus, it is the identical 3 files (showcase_capability_map, showcase_command_center_jsx, showcase_start_here; confirmed by a repo-wide grep for kind: 'html'|'jsx' outside rule source/fixtures — nothing else exists). Running validateJsxPages directly against those 3 real pages with the real repo-root sdui.manifest.json reproduces packages/lint/src/sdui-jsx-baseline.json exactly — 13 rows, 232 findings, byte-identical breakdown — confirming both that the corpus is identical and that the ledger is not stale. Per that ledger's own recorded verdict (re-verified live, not re-adjudicated fresh): 0 of the 232 are real — "every error below is a plain HTML tag, none is a real authoring mistake." So the hypothetical "if validateJsxPages were widened to runtime-publish today, using only the manifest-supply fix" number is 232 findings / 0% real / 100% noise — the same 232, because it is the same corpus.

Paired, as asked: 0/0 (real mechanism, undefined ratio, nothing ran) vs. 232 findings / 0% real (hypothetical, same corpus, already-adjudicated ledger). Neither number is an argument for urgency on its own — the first says nothing is enforced today; the second says the existing first-run-noise problem #12924's ratchet-to-zero ledger was built to hold would recur unchanged if the surface question below were answered "yes" without also fixing the same public-tier-vocabulary gap objectui#6740 already tracks.

The reframing: the card's three candidates answer "where does the value come from," not "does the rule run at all"

#13445's three candidates (kernel-boot resolution / a published manifest in an existing dependency / an explicit deployment input) are all about sourcing sduiManifest. All three are necessary but none is sufficient — solving the sourcing question and wiring it through protocol.ts would still produce the 0/0 line above, unchanged, because validateJsxPages's CLI_ONLY surface is a separate, deliberately adjudicated boundary, not a wiring gap:

packages/lint/src/authoring-rules.ts, RUNTIME_HEAVY_SOURCE_PARSE: "Not runtime-safe: parses authored source through typescript/sucrase, the two dependencies the kernel boot path must never load (lazy-deps.test.ts). Studio compiles page source on its own path."

runtime-lazy-deps.test.ts pins the claim in three tiers, machine-checked against spawned processes reading the built dist/ (not a same-process assertion): typescript/sucrase (~9 MB) load never at the runtime surface — "not at import, not gating any type, with or without any authored artifact" — for exactly this reason. Lifting validateJsxPages onto runtime-publish would need to cross that boundary (or replace the rule's parser with something the kernel boot path is allowed to load), which is a second, independent architecture decision with its own blast radius — arguably the more consequential one, since it is a "may this ever run on the hot write path at all" question, not a "which of three plumbing shapes" question. This dev seat is not recommending an answer to it — flagging that #13445's framing (three candidates, one axis) undercounted the decision by one axis is itself the finding.

The three original candidates' blast radii, as measured (not restated)

# Candidate Measured blast radius
1 Kernel-boot manifest resolution handed to the protocol Wiring choke point is small: ObjectStackProtocolImplementation's constructor has exactly ONE production call site (packages/metadata-protocol/src/plugin.ts's assembleMetadataProtocol); an added optional positional/options param is backward-compatible with all 228 test call sites that construct it directly. Sourcing is the open problem: precedent exists for boot-time deployment-fact gathering threaded as pure arguments (organizationId, orgWallEnforced via resolveTenancyPosture() — called from 10+ files including packages/runtime/src/app-plugin.ts, packages/objectql/src/engine.ts, packages/cli/src/commands/serve.ts), but every existing case is a small env-derived boolean/string, not a multi-KB generated JSON artifact requiring filesystem or network access at boot — there is no existing analog for that shape, and the CLI's own cwd-based read is explicitly out (arbitrary server-side cwd; ruled out by #13445 1.4).
2 Published manifest inside a package the runtime already depends on metadata-protocol's current deps (core, formula, lint, metadata, metadata-core, spec, types) — none ship this manifest today. The manifest is presently a cross-repo sync artifact: generated by loading a Playwright-rendered browser dump of objectui's built console app (scripts/gen-sdui-manifest.sh → packages/console/dist/sdui.manifest.json), pinned to .objectui-sha, sha256-verified and staleness-gated against that pin (scripts/check-sdui-manifest.mjs). Baking it into an existing dependency moves that generator/sync obligation onto that package's own release cadence — a new coupling axis metadata-protocol does not have today (currently only the CLI reads the always-current repo-root file, at run time, from disk). The one existing precedent for "ship it inside a dependency" — the CLI's own @objectstack/console/dist/sdui.manifest.json fallback — is independently re-confirmed broken here: packages/console/package.json's exports map is {"./package.json": "./package.json"} only, no . or ./dist/* entry, so ERR_PACKAGE_PATH_NOT_EXPORTED is not a transient issue, it is what the declared exports map guarantees.
3 Explicit deployment input Established pattern, smallest mechanical surface: AssembleMetadataProtocolOptions already carries deployment-supplied posture flags this exact way (options.authoringChannel, options.runPlatformMigrations), both optional and safely defaulted; a sibling options.sduiManifest?: unknown would fit without a new mechanism. In practice only one production call site sets authoringChannel explicitly today (packages/objectql/src/plugin.ts, forwarding its own plugin option) — the real "who has to configure this" burden centralizes at the ObjectQLPlugin options layer rather than scattering per-deployment. Cost (as the original card already named, now sharper): an unset option must default to today's parse-only behavior — same "unstated posture must not manufacture a refusal" principle already coded for orgWallEnforced — so most tenants who never opt in get no behavior change, reproducing today's status quo for the silent majority.

What remains for the maintainer

Two independent decisions, not one — and the second is prior in the sense that answering only the first (any of the three candidates) still yields the 0/0 line above:

  1. Should validateJsxPages (or an equivalent, lighter check) ever run on the runtime-publish surface, crossing the typescript/sucrase kernel-boot-path ban runtime-lazy-deps.test.ts currently enforces absolutely — and if so, how (a lighter/pre-compiled parser? an async/deferred validation pass instead of a synchronous save-time gate? accept the ~9 MB load on this one path)? This is unmeasured by this card; it is a new architecture question the original three candidates did not anticipate.
  2. If (1) is yes, which of the three candidates above sources the manifest — unchanged from [finding] the runtime save gate never receives a sduiManifest — the third independent cause of validateTree inertness, untouched by the #12924 wiring #13445's original framing, now with measured blast radii per candidate instead of estimated ones.
  3. Either way, arming it reproduces the 232-finding/0%-real noise problem [finding] nothing wires an sdui.manifest.json, so validateJsxPages runs parse-only and validateTree — including both ported lockstep diagnostics — is dead code in the production gate #12924's ratchet-to-zero ledger already holds for the CLI seam — the same public-tier-vocabulary gap (objectui#6740, div/a absent from the 57-block curated set) needs its own resolution or its own ratchet-to-zero ledger at the runtime surface before "armed" reads as "useful" rather than "noisy."

Refs

Generated by Claude Code

Activity

  1. huangyiirene commented on Sep 1, 2026

    @huangyiirene
    Collaborator

    裁决:(1) 否 —— 现在不武装;绊线入册;本卡关闭 completed(维护者 2026-09-01,总监批 #26)

    项目总监席 · session session_01KGtaLpkW1mycWgkbSb3H6t · 维护者对本批逐字:「同意」。

    1. (1) 裁「否」:validateJsxPages(或等价检查)不上 runtime-publish 面 —— typescript/sucrase 的内核 boot 路径禁令(三层 pin,对 spawned 进程验 built dist)是被刻意裁定的架构边界,⛔ 不为一个假设面收益 232 findings / 0% real 的检查打破它;运行时保存门维持 parse-only,CLI_ONLY 边界维持;
    2. (2) 随之 moot:三个 manifest 供给候选的实测 blast radius(本卡的测量交付)入档备用,⛔ 不预选;
    3. 绊线(具名可证伪):objectui#6740 的公开层词表缺口解决(div/a 进精选集或有自己的 ratchet-to-zero 台账)且 出现真实的保存路径编写消费者 ⇒ 问题 (1) 带新分母回决策箱;
    4. 测量席的方法论发现记名入册:「派发令的三候选框架少算了一个轴」本身是本卡最有价值的交付 —— 供给问题与运行面问题是两个独立裁决,后者在先;
    5. [finding] the runtime save gate never receives a sduiManifest — the third independent cause of validateTree inertness, untouched by the #12924 wiring #13445 已按其自身条款关闭,不动;[finding] nothing wires an sdui.manifest.json, so validateJsxPages runs parse-only and validateTree — including both ported lockstep diagnostics — is dead code in the production gate #12924 的 404(账号停用类,Dangling-reference patrol: nothing detects an issue reference that fails to RESOLVE (as distinct from closed) — measured 6+ times, and a card's author is unreadable once it is unreachable #13634 族)已在案。

    状态转移(同笔)

    关闭 completed(测量已交付 + 裁决已记);摘 needs-user-decision;priority:p2 domain:devx 留卡。


    Generated by Claude Code

  2. removed
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions