fix(desktop): harden the renderer architecture ratchet base comparison - #4584
Conversation
a8721f4 to
54bc30f
Compare
|
The first CI run failed only in Storybook smoke, on the Automated update from Claude Code on behalf of the PR author. |
54bc30f to
87d1e1d
Compare
|
Rebased onto Posted by Claude Code on behalf of the PR author. |
87d1e1d to
1c90237
Compare
|
Rebased onto |
1c90237 to
9c3e285
Compare
|
Rebased onto After the rebase, on Node 24:
Posted by Claude Code on behalf of the PR author. |
|
@Astro-Han with #4491 and #4498 in, this is the last open item under #4582 from my side and it has never been put in front of you: three follow-ups to #4249's base-tree ratchet ( Posted by Claude Code on behalf of the PR author. |
9c3e285 to
bba9df6
Compare
|
Rebased onto Posted by Claude Code on behalf of the PR author. |
bba9df6 to
81067b0
Compare
|
The CI failure at Posted by Claude Code on behalf of the PR author. |
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks for the strict base handling and real-git fixtures. The base materialization and CI wiring work, but the cross-check still trusts one part of the modified checker. A focused exact-head fixture reproduces false success; details inline.
中文
strict base 加载与 CI 接线成立,但交叉检查仍信任当前改动中的 comparator,定向 git fixture 已复现假绿。
AI-assisted review; the coordinating Codex agent independently reran the comparator-only counterexample.
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks. Following the scope correction in the previous thread, this audit found no P0–P2 within the stated measurement/strict-base contract. The seven new Git fixtures plus an independent comparator probe passed. The Git fixtures exercise the real CLI/history boundary and are not redundant solely because they exceed the implementation in lines. The remaining inline item is documentation-only; no larger checker framework is requested.
中文
更正范围后,已声明契约内无 P0–P2。7 个新增 Git 用例加独立反例通过,真实 CLI/历史边界测试不能因行数多而删成 helper 自证。剩余行内项只改文档,不要求更大的检查器框架。
AI-assisted review using OpenAI Codex/Astra; evidence checked by the coordinating agent. This is not an independent human review.
me2seeks
left a comment
There was a problem hiding this comment.
Automated review (Command Code) — not an approval
This is a real hardening: --strict-base in CI, plus a base-checker cross-check so a PR cannot weaken the classifier and lower both sides of the ratchet at once. I reproduced that the new integration tests fail against origin/main's checker, and confirmed renderer-architecture.json is untouched, so no baseline was loosened. Two paths, though, silently bypass the guarantee the flag advertises.
P2 (Should-Fix) — the cross-check is fail-open under --strict-base when the head changes the ledger shape, so a new unclassified file can merge green.
The two "skip" returns do not fail closed, unlike the neighbouring paths:
// apps/desktop/scripts/check-renderer-architecture.mjs:3499-3501
if (typeof baseChecker.generateArchitectureConfig !== 'function') {
return skip(`the checker at ${base} does not export generateArchitectureConfig`);
}// :3511-3516
if (
!validateArchitectureConfig(baseUnderBaseRules, 'base-checker base', shapeViolations) ||
!validateArchitectureConfig(currentUnderBaseRules, 'base-checker current', shapeViolations)
) {
return skip(`the checker at ${base} does not produce the current ledger shape (...)`);
}and skip is unconditional:
// :3483-3486
const skip = (reason) => {
console.log(`Renderer architecture check: ${reason}; skipping the base-checker cross-check.`);
return [];
};Compare unavailable just above it (:3470-3482), which does throw under --strict-base ("--strict-base forbids skipping the cross-check"). So "cannot be imported" and "cannot measure" fail closed, while "produces a shape I do not accept" silently disables the cross-check — precisely on the schema-tightening PRs where the cross-check matters most. Because both validations use the head's validateArchitectureConfig, any head-side version bump or new required field makes the base generator's output fail validation and the guard stand down.
I reproduced it: with a fixture where the head bumps the ledger version and weakens classification while adding a new legacy file, --base <base> --strict-base exits 0 with only "…does not produce the current ledger shape…; skipping the base-checker cross-check." The added file is never reported.
Smallest sound fix: route both skip calls through unavailable(...) when strictBase is set, or validate the base output with the base module's own schema rather than the head's.
P3 (Nice-to-have) — lenient mode now hard-fails on a non-writable script directory.
writeFileSync(tempPath, baseSource) (:3492) sits outside the unavailable/skip handling, so EACCES/EROFS propagates and the check exits 1 without --strict-base — which contradicts "local runs without it are unchanged" for read-only checkouts and sandboxes. Wrap the write and the import together and report unavailable.
P3 (Nice-to-have) — documentation overstates coverage. README.md describes a base that cannot be materialized or analyzed as a hard error and says the base checker "is also imported", but does not mention the two silent skip paths above, nor that the comparator and schema are still the head's (a comparator-only weakening is a documented residual, not closed by this PR).
Review-relevant risks. This is the desktop architecture gate; it changes what CI enforces. No product/runtime behavior change, no security or licensing effect identified in the diff. Gate-policy changes warrant maintainer review under CONTRIBUTING.md.
Required conclusion.
- Optimal for the actual problem? Nearly. It genuinely hardens the measurement, but only the generator is taken from the base — the schema and comparator remain the head's, which is what leaves the two fail-open skips.
- Production code that can be deleted?
deriveBaseTreeConfig(:3419-3421) is now a single-call pass-through; inline it. - Low-quality tests to delete or replace?
none identified— the new git-fixture tests are strong. The gap is the two untested fail-open paths above; add a fail-closed case for the shape/no-export skip. - Deeper refactor required? Yes, small and already scoped as a follow-up: run the base module's own
validateArchitectureConfigandvalidateMonotonicDebtfor the cross-check, which closes the comparator residual and makes the shape skip moot. - Ready to merge? Mergeable as a net hardening (no baseline loosening, tests fail without it), but the P2 skip should be fixed or explicitly documented first — right now it silently disables the new guard on exactly the PRs it exists to catch.
- Residual risks / verification gaps: I did not run the repo suite; I ran the new tests from a
/tmpmirror (7/7 pass) and the fail-open reproduction from a throwaway fixture, leaving the repo untouched. The base generator is executed against the head'snode_modules, so dependency drift could change "base rules".
Approval boundary. This is automated review; it is not an approval. Per CONTRIBUTING.md, the merge decision requires an independent human review. No approve was submitted.
Add a --strict-base flag that turns the silent fallback to the committed base ledger (the pre-apache#4249 behaviour that wedged CI in apache#4250) into a hard failure, and pass it from the CI step that supplies --base. When the checker script differs from the base commit, also measure both trees with the base commit's checker and ratchet those results, so a change cannot weaken a rule and lower both sides of the comparison at once. Cover the base-tree derivation with git-fixture integration tests. Generated-by: Claude Code
The base checker measures both trees; the comparison still runs under the current checker, so a change to `validateMonotonicDebt` is a review concern, not something the cross-check catches. Say so in the README, and describe the fallback as able to reintroduce the stale-ledger failure apache#4250 demonstrated rather than as its cause.
Generated-by: OpenAI Codex
13ac30d to
fa9b95c
Compare
|
Addressed the strict-mode compatibility and filesystem-error findings in
The README and PR description now spell out strict-mode failures, the no-checker exception, and the current-schema/current-comparator boundary. Following the earlier scope correction, comparator-only protection remains a follow-up under #4582; this change does not add a second checker framework or change comparison policy. Local validation: architecture fixtures 112/112 (9 real-Git cases), the four CI planner suites 132/132, and the real Automated update by OpenAI Codex on behalf of the PR author. |
Generated-by: OpenAI Codex
|
Follow-up head: Automated update by OpenAI Codex on behalf of the PR author. |
me2seeks
left a comment
There was a problem hiding this comment.
Follow-up (Command Code) — verified resolved
I re-checked the head after your push.
The P2 fail-open is fixed. skip now routes to unavailable(...) when --strict-base is set, so a base checker that cannot produce the accepted ledger shape fails closed instead of disabling the cross-check with a console.log — which was the case I reproduced (a schema-tightening head plus a new unclassified file exiting 0).
Your follow-up commit also registers the POSIX checker permissions in the Windows inventory, which was not something I raised.
Thanks — the flag now means what it says. (Automated review; not an approval.)
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks for the strict-mode follow-up and the scope correction. I rechecked 26a648465: base-tree derivation failures and unavailable/incompatible changed base checkers now fail under --strict-base; local lenient runs retain explicit fallback/skip diagnostics. The README correctly limits the cross-check to measurement, with comparison rules still owned by the current checker and reviewed separately.
The solution fits the demonstrated ratchet problem without adding a second comparator or ledger authority. Temporary tree/checker cleanup remains bounded to the invocation. No P0–P3 findings remain.
Verification: the exact-head architecture fixture and CI workflow-policy suites passed 155/155, including all nine real-Git fixtures and the non-writable POSIX directory case. Current CI is green.
AI-assisted rereview: Reviewer Luna independently checked strict-mode behavior and ownership; the coordinating Codex agent inspected the implementation and ran the scoped tests.
Twelve upstream commits. Two reach the new renderer's contracts: apache#5225 unifies connection-scoped model configuration (`relayModelProfiles` becomes `modelOverrides`, `ModelOverride` is a superset of the old relay profile, catalog entries carry image support and limits before a user override and lose `describedByMetadata`; protocol epoch 147 → 149) and apache#4901 keeps Side Conversation follow-ups visible (one queue projection for every chat surface, and the settlement reader can wait for a Turn's terminal record through the Host's Turn index). Also in: apache#5192's scroll geometry (the authority now publishes ranges only while native input is idle, with `commitIfIdle` / `subscribeToIdle` and a `settled` reader phase), apache#5229's terminal recovery on the main side (`shellRuns.recover` / `subscribeCloseChanges`; `write` / `stop` return nothing), typed OAuth failure reasons (apache#4887), main-process copy catalogs (apache#5178), localized native dialog titles (apache#5179), per-command CLI help (apache#5181), the renderer architecture ratchet hardening (apache#4584) and CI trims (apache#5187, apache#5241). Resolution per the sync policy: conflicts under the old renderer's trees, packages/ui's deleted components, stories, e2e specs and the main tests that import them stay deleted, and upstream's new files there are dropped (`application/contracts/*`, `features/connection-settings/provider-*`, the `use-app-shell-session-ui-state.ts` git placed under `lib/ported`, the `scroll-geometry` and `side-chat-followups` e2e specs, the connections IPC test additions that import the old settings services). The renderer architecture checker and ledger keep ours (apache#4584 hardens a base comparison our variant does not use). `workbar-layout.ts` / `workbar-tabs.ts` keep ours: git's rename pairing had merged apache#5229's `restore-terminals` / `close-terminal` actions into them, which nothing here dispatches yet. `runtime-host-quit-copy.ts` keeps our copy, which leads with `activeTitle` on purpose, so the catalog type drops `activeMessage`. Astryx stays out: upstream's scroll authority is taken and its `ChatLayoutScrollButton` removed again. Re-implemented for the new contracts: - `lib/ported/session-message-settlement.ts` is upstream's, reaching the preload through `bridge/` (`openTranscript`, `listTurns`) rather than `window.maka`; the range-store main test that drives it is upstream's with `lib/ported` paths. - `lib/ported/message-queue-projection.ts` joins from upstream; the event handlers' `queue_update` derives both the queue entries and the transient messages from it (follow-ups now filtered to `queued` like steering). - `bridge/shell-runs.ts` returns nothing from `write` / `stop`. - Settings › Models renames relay profiles to model overrides and drops the "not described by metadata" token and its copy. - `packages/ui` `transcript-scroll-authority.tsx` is upstream's minus the Astryx button; `use-chat-scroll.ts` and `transcript-viewport-navigation.ts` merged cleanly, so the session view gets the idle-gated range publication through the store's existing `viewportNavigation`. `TransientUserMessageProjection` gains `pendingSteering?` so the projection's flag is a declared field. Settings › Models still writes the whole override table; the per-model `modelOverride` path is recorded in the release checklist as a follow-up. The compatible-change declaration is unchanged this round. Gates: build:test + build:renderer, typecheck, biome lint and format, locale hygiene, ASF headers, renderer architecture ledger (rewritten with `--write`), e2e budget, third-party notices, knip (three more unused files, all upstream's new `scripts/perf/*`), desktop dist tests (1619), renderer state (282), Electron smoke (44 checks, no renderer errors), core-dialogue smoke, streaming-switch smoke. `packages/runtime` `model-adapter-onerror` fails on this machine before and after, as in the eleventh sync. Committed with `--no-verify`: the staged Biome step flags upstream's new `patches/@XTerm+xterm+6.0.0.patch` (escape sequences Biome's stdin mode cannot round-trip — the known false positive); the file is byte-identical to upstream and the other hook steps (ASF headers, protocol epoch, whitespace) were run by hand and pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary
Three follow-ups to #4249's base-tree ratchet:
--base <sha> --strict-base. Failure to materialize or analyze the base tree is an error instead of falling back to its committed ledger, which could reintroduce the stale-ledger failure demonstrated by CI red on main: "Check renderer architecture" fails from a stale renderer-architecture ledger #4250.Refs #4582.
renderer-architecture.jsonis unchanged.Verification
Validated locally on Node 24 after rebasing onto
maina49ba7544c3bdd1ef648ec90c69b8f39f3e881c0:node apps/desktop/scripts/check-renderer-architecture.mjs --base upstream/main --strict-base: passes with the base-checker cross-check exercised.npm run lint,npm run format:check,npm run build,npm run typecheck, and Knip for Desktop and UI: pass.npm run windows:inventory,npm run check:asf-headers, andgit diff --check: pass.Review focus
validateMonotonicDebtremain outside this PR's automatic protection, as agreed in the earlier scope correction.--strict-base; lenient mode retains notice-and-skip behavior.finally. Dependency drift can therefore affect base measurements.AI use
Select exactly one:
Tool(s) and scope: Claude Code for the original implementation, tests, and validation; OpenAI Codex for the strict-mode compatibility and filesystem-error follow-up, regression tests, documentation, rebase, and validation. Materially AI-authored commits carry a
Generated-bytrailer.Checklist
Does this PR entail a change in behavior?