You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[feat](advisor): the "no offsetting" check — does the pick survive without compensation?
Step 2 of the formula work, and a correction to my own plan: I had proposed this
as a separate button. That was wrong twice over — a control the user must first
discover is the exact discoverability failure fixed elsewhere in this app, and a
second warning panel would compete with the compensation disclosure rather than
complete it. It is now one line attached to that disclosure, which turns "there is
a weakness here" into the question that actually matters: does the DECISION depend
on it?
"Microservices scores 2/5 on Cost efficiency, which you weighted 13.2% …
Score weakness more heavily and Serverless (FaaS) wins instead —
this pick leans on offsetting."
Computed only when a compensation exists; on its own the verdict has nothing to
qualify. Additive stays the recommendation — this is a second opinion, and a test
asserts running it cannot change what rank() returns.
I OVERCLAIMED, AND THE TEST CAUGHT ME. The first doc comment said a geometric mean
means "no amount of strength elsewhere fully recovers" a weakness. That is false,
and my own test failed proving it: with twelve equally-weighted attributes, one fit
of 1 among eleven 5s scores 4.37 — comfortably above a flat row of 3s. Geometric
aggregation REDUCES compensability; it does not remove it. Strength still buys off
weakness, it simply pays a worse rate. Genuine non-compensation needs a veto or
threshold rule (ELECTRE-style), which would need its own ADR. The comment now says
this plainly and two tests pin it: one proves the aggregators disagree inside the
band where they genuinely do (~32–50% weight on the weak attribute), the other
asserts the equal-weight case where the weakness IS bought off, so the overclaim
cannot come back.
That correction also applies to PR #70's description, which called geometric a
proper fix for compensability. It is a partial one.
GUARD FIX, NOT A GUARD DODGE. check-language-purity flagged the new Indonesian
string for containing the English word "other" — from the `{other}` placeholder.
Placeholders are substituted at runtime: they are code, not prose, and reading them
as language is simply wrong. The guard now strips `{…}` tokens before analysis
rather than the string being reworded to slip past it. Re-verified it still bites.
BUNDLE 286→287 total (measured 286.2). Being explicit about which promise applies:
the note on the INITIAL budget says a third raise there should trigger splitting the
below-the-fold Advisor results into their own chunk. That is a first-paint concern
and this is not it — initial sits at 122.5/123 and barely moved. This is the NFR
ceiling guard (cap 300), measuring the whole app rather than what loads first.
Gates: build clean, lint 0, 265/265 unit, 15/15 E2E, 7/7 guards, 0 axe in both
languages and both themes at 390px.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: scripts/check-bundle-size.mjs
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -17,7 +17,7 @@ import { gzipSync } from 'node:zlib';
17
17
// shared async chunk can't silently be mis-counted. Headroom catches a real regression; raise the
18
18
// budgets deliberately (with a note) if the app grows.
19
19
constJS_INITIAL_BUDGET_KB=123;// History, so the pattern stays visible: 120→121 Phase 3 (lazy chunks, stub only). 121→122 briefly on 2026-07-25 for Insights prose, RETURNED to 121 the same day by moving that prose to the lazy chunk — avoidable, and the fix was structural. 121→122 on 2026-07-27 for decision leverage. 122→123 on 2026-07-27 for the compensation disclosure (measured 122.2). THIS IS THE SECOND RAISE IN TWO CHANGES and both are eager Advisor code, so the trend deserves watching, not waving through. Trimmed first, not after: the disclosure prose was cut ~55%, and a newly-imported Tabler icon was dropped for IconScale which the file already had (that one alone brought TOTAL back under budget). The residual ~0.2kB is the feature itself on the default tab. If a third raise comes up, the answer is probably to split the Advisor results below the fold into their own chunk rather than to move this number again.
20
-
constJS_TOTAL_BUDGET_KB=286;// raised 200→260 (Insights bilingualisation 2026-07-15); 260→268 for the Phase 3 Chat Advisor 2026-07-19 (adapter + hook + panel, all in a LAZY chunk — the FAB is lazy too, so the initial budget is untouched); 268→278 for the 2026-07-23 scenario-coverage expansion (cost/ops, risk catalog, sensitivity, migration, dimension/factor/QA lookups, app-usage FAQ — all pure data-driven text in the same lazy chunk; NFR cap is 300); 278→281 for the 2026-07-25 Chat Advisor capability browser + "How to use this" pane (bilingual help copy, same lazy chunk — initial JS unchanged at 120.8/121, so first paint is unaffected); 281→284 for the 2026-07-25 role-based "Start here" section in the Guide (six bilingual role paths — prose only, in the already-lazy ManualBook chunk that loads on demand; initial JS still 120.8/121, NFR cap 300) ; 284→286 on 2026-07-25 for the seven deepened Insights section intros — real added prose, but it now lives in the LAZY Insights chunk rather than the eager dict, which is why the initial budget went DOWN in the same change. NFR cap is 300.
20
+
constJS_TOTAL_BUDGET_KB=287;// raised 200→260 (Insights bilingualisation 2026-07-15); 260→268 for the Phase 3 Chat Advisor 2026-07-19 (adapter + hook + panel, all in a LAZY chunk — the FAB is lazy too, so the initial budget is untouched); 268→278 for the 2026-07-23 scenario-coverage expansion (cost/ops, risk catalog, sensitivity, migration, dimension/factor/QA lookups, app-usage FAQ — all pure data-driven text in the same lazy chunk; NFR cap is 300); 278→281 for the 2026-07-25 Chat Advisor capability browser + "How to use this" pane (bilingual help copy, same lazy chunk — initial JS unchanged at 120.8/121, so first paint is unaffected); 281→284 for the 2026-07-25 role-based "Start here" section in the Guide (six bilingual role paths — prose only, in the already-lazy ManualBook chunk that loads on demand; initial JS still 120.8/121, NFR cap 300) ; 284→286 on 2026-07-25 for the seven deepened Insights section intros — real added prose, but it now lives in the LAZY Insights chunk rather than the eager dict, which is why the initial budget went DOWN in the same change. NFR cap is 300. ; 286→287 on 2026-07-27 for the non-compensatory check (measured 286.2). To be explicit about which promise applies: the note on JS_INITIAL_BUDGET_KB says a third raise there should trigger splitting the below-the-fold Advisor results into their own chunk. That is a FIRST-PAINT concern and it is not this — initial sits at 122.5/123 and did not move enough to matter. This budget is the NFR ceiling guard (cap 300), measuring the whole app rather than what loads first, so a 0.2kB step for a genuinely new engine function plus two short strings is what it is meant to absorb.
21
21
constCSS_BUDGET_KB=29;// 25→27 (Fase 2g polish 2026-07-18); 27→29 for Phase 3 2026-07-19 (chat panel + copilot overlay/launcher/Dos-Don'ts cards); still under the ~30kB NFR ceiling
Copy file name to clipboardExpand all lines: src/i18n/dict.ts
+5Lines changed: 5 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -86,6 +86,11 @@ export const DICT = {
86
86
en: '{option} scores {fit}/5 on {qa}, which you weighted {weight}% — its strengths elsewhere outweigh that. Check the trade is one you can accept.',
87
87
id: '{option} bernilai {fit}/5 pada {qa}, padahal Anda memberinya bobot {weight}% — kekuatannya di tempat lain menutupi hal itu. Pastikan pertukaran itu bisa Anda terima.',
88
88
},
89
+
'comp.holds': {en: 'Still wins even when weakness counts double.',id: 'Tetap unggul meski kelemahan dihitung lebih berat.'},
90
+
'comp.flips': {
91
+
en: 'Score weakness more heavily and {other} wins instead — this pick leans on offsetting.',
92
+
id: 'Bila kelemahan dinilai lebih berat, {other} yang unggul — pilihan ini bertumpu pada penutupan kelemahan.',
0 commit comments