GATE 9 — Corporate "risk-team-in-a-box" report: critique pass v1
Date: 2026-06-05 14:47 CEST / 12:47 UTC Wake: wake-strategy Scope: the productionised corporate exposure report at app/intelligence/dossiers/[slug]/report/page.tsx (341 lines, last touched pre-2026-06-01 pivot) and its components/PrintButton.tsx companion. Method: read the source; live-rendered against two structurally-different dossiers (umi-br, asml) via curl http://localhost:3010/intelligence/dossiers/<slug>/report; compared the rendered output against GATE 9's pass test ("a corporate exec reads one report in 10 minutes and walks away with (a) their top-3 material/policy exposures quantified, (b) the specific laws that threaten them, (c) what likely escalates next, (d) what to do — each traceable to a primary source. It must read like a Control-Risks / strategy-consultancy deliverable, not a data dump."). Outcome: report does not yet pass GATE 9; gate stays building. This critique catalogues seven findings, graded by severity, with file:line pointers and named owner per fix.
Headline
The report has the shape of a strategy-consultancy deliverable (verdict box, exposure register, laws list, mitigations, evidence, methodology) but loses to the test on three structural failures: (1) a rendering bug that prints raw markdown bracket-syntax inline in the dossier-body block; (2) a scope-mismatch — the entire research-register dossier body is dumped into the report instead of an executive précis; (3) non-differentiated forward-looking and mitigations content — the same boilerplate ships for every company. These three are gate-blocking. Four further findings are polish-level (verdict density, "as-of" semantics, no delta panel, attribution-mismatch on indirect-exposure firms).
The buyer-pivot success metric in §8 of docs/strategy/PIVOT_2026-06-01_economic_statecraft_layer.md requires a corporate-exec reader to read one report and say "this is what I'd have paid a strategy team to produce." Today's report fails on first contact when the dossier body renders with literal [link](../../iptm/actions/...) brackets visible in the prose — a polish failure no consultancy deliverable would ship with. Fixing the rendering bug alone is necessary but not sufficient; finding F2 (scope mismatch) is the deeper structural issue.
Findings (graded)
F1 — GATE-BLOCKING — dossier body renders raw markdown links as bracket text
What I saw. Live curl of /intelligence/dossiers/umi-br/report: `` 2024-05-23 [EU CRMA entry into force](../../iptm/actions/2024-05-23-eu-crma-entry-into-force.md) (Reg. 2024/1252) 2025-03-25 [EU CRMA Strategic Projects — first designation](../../iptm/actions/2025-03-25-eu-crma-strategic-projects-first-designation.md) (47 EU + 13 third-country) 2023-08-17 [EU Battery Regulation 2023/1542](../../iptm/actions/2023-08-17-eu-battery-regulation-2023-1542.md) ` These are inside HTML <td> cells — i.e. they made it into the rendered DOM as visible text, brackets and all, not as <a href> tags. The dossier .md files use the docs-tree relative path convention (../../iptm/actions/<slug>.md) which renderMarkdown (called at app/intelligence/dossiers/[slug]/report/page.tsx:154) is not normalising to the web route /actions/<slug>. The two link-rendering paths in the report diverge: the "Laws that threaten it" section (the dedicated ol) correctly resolves to /actions/<id> (verified — 16 /actions/...` hrefs in the rendered page), while the dossier-body block resolves to nothing.
Why it fails the test. A board-ready report cannot ship with literal markdown syntax visible in body prose. This is the single most exec-legibility-destroying defect in the current report.
Recommended fix (UI scope — wake-graph). Two options, pick one:
- (preferred) In
app/intelligence/dossiers/[slug]/report/page.tsx:81wherestripFrontmatter(doc.markdown)returns the body, run a regex pre-pass that rewrites(../../iptm/actions/<slug>.md)→(/actions/<slug>)and(../../minerals/materials/<slug>.md)→ (a real materials route if it exists; else strip the link and keep the label text). Pure-string transform, no markdown-parser change. - (alternative) Don't render the dossier body at all (see F2) — solves this defect as a side-effect.
Owner. wake-graph (UI file). Strategy queues to ops/queue/graph.md next G8/G9 tick.
F2 — GATE-BLOCKING — full dossier body is dumped, not an executive précis
What I saw. The Umicore report renders the entire docs/intelligence/dossiers/umi-br.md body inside the report — including the "What they do" multi-paragraph corporate profile, the cross-references list (docs/minerals/materials/cobalt.md, ...nickel.md, ...germanium-gallium.md, ...pgm.md — internal repo paths, broken in the rendered report), the full "Policy actions touching them (last 24 months)" table (which then duplicates the dedicated "The laws that threaten it" register section three blocks below), and the "Strategic alternatives" / "Open questions" sections that are research-register content, not exec-report content. Total dossier-body rendered length for umi-br is ~2,000+ words, before the report's own scaffolded sections.
The mismatch is structural: the dossier is the research seed (long, structured, multi-section, written for an analyst); the report is the executive deliverable (one-page-equivalent, exec-scannable, written for a CEO). Today they are the same content, twice-styled.
Why it fails the test. GATE 9's test specifies the exec reads in 10 minutes and walks away with four enumerated takeaways. With the current double-body shape, an exec spends the first 5 minutes reading the dossier's "What they do" + "Material and trade exposures" + "Policy actions touching them" narrative — and then encounters the same register again as "The laws that threaten it." Symmetric duplication is a consultancy-credibility killer.
Beyond duplication, the dossier body contains content that does NOT belong in an exec report: cross-reference paths, methodology-style language, analyst-voice editorial caveats. These are right for the dossier (research seed) and wrong for the report (deliverable).
Recommended fix (multi-tick; needs dossier-frontmatter convention + UI change). 1. Add three new optional frontmatter fields to the dossier convention: exec_summary: (4-6 sentence one-paragraph, no markdown links, written as deliverable-tone), binding_thesis: (≤140-char sentence, what the binding chokepoint actually means for the company), escalation_watch: (3-bullet list of NAMED forthcoming events / draft rules / regulatory deadlines on the binding material — see F3). Dossiers without these fields fall back to a graceful "executive summary not yet written" placeholder. 2. In app/intelligence/dossiers/[slug]/report/page.tsx: replace the dossier-body section (lines 145-156) with a new "Company profile" block that renders exec_summary only, not the full body. The dossier body remains accessible at /intelligence/dossiers/[slug] (the non-/report route) for analyst-tier readers. 3. The dossier-body link-defects (F1) become moot in the new shape because exec_summary is deliverable-prose, link-free.
Owner. Frontmatter convention = strategy (already in scope — additive frontmatter on new files, not editing existing prose bodies, allowed per safety scope). UI change = wake-graph.
Sequence: the next G9 tick can ship the frontmatter convention as a chunk-1 by writing exec_summary: for the top-5 dossiers (e.g., umi-br, asml, basf, eramet, lkab) and updating the dossier README. The UI change waits for wake-graph.
F3 — GATE-BLOCKING — "What to watch next" is a count-restatement, not a forecast
What I saw. Template output for Umicore: > "Neodymium is the line to war-game: CN already controls 85% of refining, and the policy lever is active. A single new licensing or export-control action on this material moves the binding score materially."
This is a tautology — it tells the reader the binding material is binding. It also tells them, in the second bullet, that secondary materials have N restrictive measures (count restatement of the register). It does NOT name a specific forthcoming event, draft rule, or regulatory deadline.
GATE 9's test requires (c) "what likely escalates next" — specifically. A strategy consultancy would name: "MOFCOM Ann. 61+62 implementing regulations are in 30-day public consultation through 2026-XX-YY; expect amended scope on tungsten downstream products" — i.e. a named, dated, traceable forthcoming event tied to a primary source.
Why it fails the test. Generic boilerplate fails the consultancy-deliverable bar. Worse: a CEO reading the same boilerplate across two different companies (say umi-br and asml — both have CN as the binding controller) would see structurally identical "what to watch next" sections and conclude the report is a fill-in-the-blank template. Differentiation is the whole point of a paid deliverable.
Recommended fix. The new escalation_watch: frontmatter field (F2) carries 3 hand-written bullets per dossier, each citing a specific event/deadline/draft-rule with a primary-source URL. The report replaces the current templated "What to watch next" content (lines 245-258) with the dossier's escalation_watch bullets. Where escalation_watch is absent, the section is hidden entirely (better empty than generic).
Owner. Dossier authors (wake-company-fill / strategy). UI = wake-graph (replace templated bullets with frontmatter pass-through).
Authoring discipline note for future dossier ticks. Every new G2 dossier should ship with escalation_watch: populated. For the 30 existing dossiers, a backfill chunk-by-chunk (10 dossiers per G9 tick) is reasonable.
F4 — POLISH — verdict box is one dense sentence, fails the 30-second-scan test
What I saw. Umicore verdict (rendered): > "The binding exposure is Neodymium — 🇨🇳 CN controls 85% of global refining. On this company's production footprint that scores 90/100 (adversarial chokepoint; global 82). The register holds 57 restrictive government measures touching this company's materials — each traced to its primary source below."
Three clauses, four numbers, two flag emojis, and a footnote-style annotation about traceability. An exec scanning in 30 seconds will pick up "Neodymium," "CN," "90/100," "57 measures" — but the meaning of 90/100 is not anchored (Critical band only signposted in the table three sections below); the 30-second reader does not know whether 90/100 is high, expected, or alarming.
Why it falls short (not gate-blocking). The report's existing band system (Critical / High / Elevated / Moderate / Low, defined in bandFor() at lines 29-35) is not surfaced inside the verdict box itself. A reader has to scroll to the register table to learn 90 = "Critical."
Recommended fix. In the verdict box (app/intelligence/dossiers/[slug]/report/page.tsx:118-143), prepend the band-label visually above the dense sentence. E.g. a 16pt amber-bordered chip reading CRITICAL — 92/100 ABOVE the prose, with the prose serving as the justifier. Compresses scan time from ~10s to ~2s.
Owner. wake-graph (UI). Trivial chip-add to existing verdict component.
F5 — POLISH — "As of" date is render-time, not register-state date
What I saw. Line 75: const asOfStr = asOf.toISOString().slice(0, 10) — i.e. the current wall-clock date of report rendering, not the latest-action date of the underlying register snapshot. Two reports of the same company rendered an hour apart will show different as of dates with identical content.
Why it falls short. A board-ready PDF should cite the data state (last action date in the register, or the register snapshot date), not the moment-of-rendering. Auditability suffers: a CEO who saves the PDF on 2026-06-05 and re-reads it on 2026-09-10 has no way to tell which actions were known at render time.
Recommended fix. Use max(cr.materialActions[].announced_date) for the as of field. Add a separate, smaller "Generated" timestamp in the footer for the render-time. Two clear semantics: data state (top) vs. render time (bottom).
Owner. wake-graph (UI), 5-line change to lines 74-75 + 110-115 + 334-336.
F6 — POLISH — no "what changed since last quarter" delta panel
What I saw. Each report is a fresh point-in-time view. There is no "since your last report, the following changed" section. A corporate buyer who reads quarterly will not be able to identify which actions are NEW vs. which were already on the register at last read — they have to remember the prior state themselves.
Why it falls short. Delta is the highest-value section of a recurring deliverable. Without it, the report's marginal value to a returning reader drops sharply — they read the whole thing again to find the 2 new rows.
Recommended fix. Two paths, choose:
- (lightweight) URL parameter
?since=YYYY-MM-DDthat filters the laws section to actions announced on/after that date, with a distinguishing visual chip ("NEW SINCE 2026-03-01"). Reader-driven, no persistence needed. - (heavier) A
lib/report-deltas.tssnapshot mechanism that stores prior reads and computes deltas server-side. Out of scope for v1.
Owner. wake-graph (UI). Lightweight version is a ~15-line change.
F7 — STRUCTURAL — attribution mismatch on indirect-exposure firms (ASML case)
What I saw. ASML report names Tungsten as the binding exposure (90/100 controlled by CN). For most readers familiar with ASML, this is jarring: ASML's policy exposure is overwhelmingly the chip-control regime (BIS/Diffusion Framework/Affiliates Rule, NL DUV-export licensing), not a critical-mineral chokepoint. Tungsten is used in ASML's optics/end-effector hardware, but the strategic risk to ASML's business model from CN tungsten controls is one to two orders of magnitude smaller than the strategic risk from the chip-control regime.
The report's scoring engine (in lib/company-risk.ts, which is OUT of strategy safety scope) ranks by material-supply chokepoint. For companies whose policy exposure is dominantly indirect-via-customer or via-regulated-output (ASML, NVDA, Soitec, etc.), the binding-material score is mathematically correct but strategically misleading as the report's headline.
Why it falls short (cross-gate, structural). The corporate-report's headline framing implicitly claims the binding material IS the binding business risk. For ~20% of the dossier corpus (semis-equipment, semis-design, defence-equipment) that claim is structurally wrong. A CEO of ASML reading the report would dismiss it as "not how my business works" within 60 seconds.
This is also a finding that reinforces GATE 8's chunk-1 audit conclusion: composite scores saturate; multi-column attribution is what FIs need. The corporate report should similarly surface the exposure type (material-direct / customer-indirect / regulated-output / dual-use) alongside the binding-material score, so the headline reads true for the company's actual exposure shape.
Recommended fix (multi-tick, structural). 1. Add exposure_shape: frontmatter field per dossier with allowed values: material-direct | material-indirect-via-customer | dual-use-regulated-output | composite. Hand-authored, one-shot per dossier. 2. Report verdict box reads the field and frames the headline accordingly. ASML's verdict would lead with "Dual-use regulated output — chip-control regime is the binding constraint" rather than "Tungsten is the binding exposure." 3. The material register table still surfaces tungsten et al. as supporting data, not headline.
Owner. Strategy (frontmatter convention) + wake-graph (UI). Multi-tick.
Cross-gate note. This finding pairs with GATE 8's companyScore saturation note — both are surfacing the same underlying issue: the existing engine's scalar output is necessary but not sufficient as the buyer-facing headline. The pivot doc's MSCI-ESG-analogy carries here too.
Roll-up: gate-flip readiness
| Finding | Severity | Owner | Tickable now? |
|---|---|---|---|
| F1 — dossier markdown links render raw | Gate-blocking | wake-graph | yes (queue this tick) |
| F2 — full dossier body dumped, not exec précis | Gate-blocking | strategy + wake-graph | yes (frontmatter chunk first) |
| F3 — "what to watch" is count-restatement | Gate-blocking | dossier authors + wake-graph | yes (frontmatter convention) |
| F4 — verdict dense, fails 30-sec scan | Polish | wake-graph | post-F1/F2 |
| F5 — "as of" date semantics | Polish | wake-graph | trivial |
| F6 — no delta panel | Polish | wake-graph | post-F1/F2 |
| F7 — indirect-exposure attribution mismatch | Structural | strategy + wake-graph | multi-tick |
To flip GATE 9 to `passing`, the minimum bar is F1 + F2 + F3 resolved. F4-F7 are non-blocking polish/structural improvements that compound credibility.
Strategy-scope chunks for upcoming G9 ticks (additive only, within safety scope):
- G9 chunk-1 (next G9 tick): Add
exec_summary:+binding_thesis:+escalation_watch:+exposure_shape:to the dossier README convention (docs/intelligence/dossiers/README.md) as new optional fields, and populate them on 5 priority dossiers (umi-br, asml, basf, eramet, lkab). Strategy scope ✓ (new frontmatter on existing dossier files is allowed — additive only, not editing prose body). - G9 chunk-2: Backfill same 4 frontmatter fields across the remaining 25 dossiers, 10 at a time, over ~3 ticks.
- G9 chunk-3: Once UI side ships (wake-graph), strategy critiques the new shape under the same gate test and flips state if passing.
Wake-graph chunks flagged to ops/queue/graph.md (next non-overlapping G8/G9 tick):
- (a) Fix F1 — link-rewriter pre-pass in report page.
- (b) Replace dossier-body dump with
exec_summaryfrontmatter passthrough. - (c) Replace templated "what to watch next" bullets with
escalation_watchfrontmatter passthrough. - (d) Surface band-label chip in verdict box (F4).
- (e)
as_ofsemantics fix (F5). - (f)
?since=YYYY-MM-DDdelta filter (F6). - (g)
exposure_shape-conditional verdict framing (F7).
Project opening? No project file opened this tick — the work cleanly decomposes into per-tick chunks under the existing G9 gate, no multi-tick coordination across wakes warrants a project doc yet. Open if scope expands beyond the F1-F3 minimum.
Cross-gate notes
- GATE 8 reinforcement. F7 (indirect-exposure mismatch) and the F4 verdict-density finding reinforce GATE 8 chunk-1's headline conclusion: composite scores saturate, multi-column attribution is what institutional consumers need. The corporate-report and portfolio-roll-up surfaces should evolve in parallel against the same anti-saturation discipline.
- GATE 2 dossier authoring discipline going forward. Newly-shipped dossiers (#31 onward) should ship with
exec_summary:+binding_thesis:+escalation_watch:+exposure_shape:populated from the start. Adds ~150 words to the authoring task per dossier. Worth it. - Feedback adherence.
feedback_no_internal_docs_as_sources.mdis honoured by the existing Evidence section (lines 296-313 — primary sources from each action's frontmatter). VerifiedprimarySources()filter at line 61:s.type === 'primary'only. No subscription or internal-docs leak path.
Reproducibility
Findings were generated from live renders against the running PM2 instance: `` curl -s http://localhost:3010/intelligence/dossiers/umi-br/report curl -s http://localhost:3010/intelligence/dossiers/asml/report ` Source-code references are to app/intelligence/dossiers/[slug]/report/page.tsx at commit 4b6ee941` head (2026-06-05 06:59Z, the GATE-8 chunk-1 commit). No file edits this tick.
End of critique v1.