loki-mode 9.81.0 → 10.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/dashboard/__init__.py +1 -1
  4. package/docs/INSTALLATION.md +1 -1
  5. package/docs/v10/BOARD.md +64 -34
  6. package/docs/v10/METRICS.md +17 -0
  7. package/docs/v10/MODERNIZE.md +399 -0
  8. package/docs/v10/PROGRESS.md +26 -0
  9. package/loki-ts/dist/loki.js +373 -354
  10. package/mcp/__init__.py +1 -1
  11. package/package.json +1 -1
  12. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  13. package/web-app/dist/assets/{AdminPage-BsKDNH7g.js → AdminPage-D4CK9wZS.js} +1 -1
  14. package/web-app/dist/assets/{Avatar-eQ64Y3hv.js → Avatar-BLMuC61x.js} +1 -1
  15. package/web-app/dist/assets/{Badge-b82yzJcN.js → Badge-kDgO8yAn.js} +1 -1
  16. package/web-app/dist/assets/{Button-Bd9FSMso.js → Button-CxzS2jHa.js} +1 -1
  17. package/web-app/dist/assets/{CockpitPage-DjnDt5xn.js → CockpitPage-CKfKewh2.js} +1 -1
  18. package/web-app/dist/assets/{ComparePage-D2gfvgOy.js → ComparePage-InmGOS5C.js} +1 -1
  19. package/web-app/dist/assets/{ErrorBoundary-BUHNHWWu.js → ErrorBoundary-tMR2rRSK.js} +1 -1
  20. package/web-app/dist/assets/{EvidenceReceiptPanel-D1I4Ttbh.js → EvidenceReceiptPanel-CbbMSEPA.js} +1 -1
  21. package/web-app/dist/assets/{GitHubIssuesPanel-DwVEFUMV.js → GitHubIssuesPanel-BpdPk3B6.js} +1 -1
  22. package/web-app/dist/assets/{GitHubPRsPanel-ConSaimn.js → GitHubPRsPanel-DkWNIYXl.js} +1 -1
  23. package/web-app/dist/assets/{HomePage-CKE8CosL.js → HomePage-CnNit9F3.js} +2 -2
  24. package/web-app/dist/assets/{LoginPage-D8axj5IL.js → LoginPage-CqshUuFx.js} +1 -1
  25. package/web-app/dist/assets/{MagicPage-aD3MFjIp.js → MagicPage-DuYwXDLp.js} +1 -1
  26. package/web-app/dist/assets/{MetricsPage-BdZxBPG9.js → MetricsPage-DSHxYTg1.js} +1 -1
  27. package/web-app/dist/assets/{NotFoundPage-TDGVwyxo.js → NotFoundPage-BCbJ-HOF.js} +1 -1
  28. package/web-app/dist/assets/{ProjectPage-CUx32MoW.js → ProjectPage-B1Re9Huw.js} +3 -3
  29. package/web-app/dist/assets/{ProjectsPage-CHFOim84.js → ProjectsPage-Cirblyo8.js} +1 -1
  30. package/web-app/dist/assets/{SettingsPage-BM4FDVIk.js → SettingsPage-BZ7q23EF.js} +1 -1
  31. package/web-app/dist/assets/{ShowcasePage-nki5E019.js → ShowcasePage-DSTIjCnB.js} +1 -1
  32. package/web-app/dist/assets/{SystemSettingsPage-B40QOcn3.js → SystemSettingsPage-CATfA96_.js} +1 -1
  33. package/web-app/dist/assets/{TeamsPage-CKsaamvM.js → TeamsPage-CIaF0o9W.js} +1 -1
  34. package/web-app/dist/assets/{TemplatesPage-D3dEJ11t.js → TemplatesPage-ShWJo0m9.js} +1 -1
  35. package/web-app/dist/assets/{TerminalOutput-DCL5jtSd.js → TerminalOutput-CcQrj1_2.js} +1 -1
  36. package/web-app/dist/assets/{activity-D-IlrNbY.js → activity-BVtnBEfY.js} +1 -1
  37. package/web-app/dist/assets/{bell-m1luNYs4.js → bell-DBu1CFm4.js} +1 -1
  38. package/web-app/dist/assets/{bot-Cl1HRT_0.js → bot-6ZTuUUMp.js} +1 -1
  39. package/web-app/dist/assets/{check-3lLURBsJ.js → check-CVN-gaNx.js} +1 -1
  40. package/web-app/dist/assets/{chevron-left-D02P_bEW.js → chevron-left-Bijd73Wy.js} +1 -1
  41. package/web-app/dist/assets/{circle-alert-ZCI8JfmJ.js → circle-alert-BMHFcde6.js} +1 -1
  42. package/web-app/dist/assets/{clock-D3VuRQUy.js → clock-6bIhCQRV.js} +1 -1
  43. package/web-app/dist/assets/{cloud-D0bikLQP.js → cloud-qjV89QLm.js} +1 -1
  44. package/web-app/dist/assets/{code-xml-tqtfp834.js → code-xml-eXgie64_.js} +1 -1
  45. package/web-app/dist/assets/{database-C2bSuVEi.js → database-CVdvn1bh.js} +1 -1
  46. package/web-app/dist/assets/{dollar-sign-Dp8f9cxy.js → dollar-sign-Bj8njgBH.js} +1 -1
  47. package/web-app/dist/assets/{file-code-corner-CNsBLnGJ.js → file-code-corner-EQhoe2_C.js} +1 -1
  48. package/web-app/dist/assets/{file-plus-D5gsIm0p.js → file-plus-BgbVRLTy.js} +1 -1
  49. package/web-app/dist/assets/{globe-C5wH6C2_.js → globe-CXt7geT_.js} +1 -1
  50. package/web-app/dist/assets/{hammer-BbgvMFD3.js → hammer-CG25oMdn.js} +1 -1
  51. package/web-app/dist/assets/{index-C-AhysBW.js → index-CVNF29Aq.js} +2 -2
  52. package/web-app/dist/assets/{layers-Cy9T0QFp.js → layers-DtbE1x2T.js} +1 -1
  53. package/web-app/dist/assets/{loader-circle-Bxmwkkrf.js → loader-circle-BUSbPfxc.js} +1 -1
  54. package/web-app/dist/assets/{lock-C8DRY3rX.js → lock-DwgGTz0p.js} +1 -1
  55. package/web-app/dist/assets/{package-0ny9RvN9.js → package-BbZAXa7s.js} +1 -1
  56. package/web-app/dist/assets/{plus-B7Dhrqtb.js → plus-OYQ2xEFD.js} +1 -1
  57. package/web-app/dist/assets/{refresh-cw-CkQrR-L1.js → refresh-cw-lx_MQBsW.js} +1 -1
  58. package/web-app/dist/assets/{rotate-ccw-du8wLFBD.js → rotate-ccw-DbqbQvZd.js} +1 -1
  59. package/web-app/dist/assets/{scroll-text-lC55WYYu.js → scroll-text-BGZMcNSM.js} +1 -1
  60. package/web-app/dist/assets/{server-DWbmrgQz.js → server-CECrh_vY.js} +1 -1
  61. package/web-app/dist/assets/{shield-alert-D7HIrslo.js → shield-alert-CMOPamqF.js} +1 -1
  62. package/web-app/dist/assets/{thumbs-up-CXHDiQ_F.js → thumbs-up-w1t0ZuDt.js} +1 -1
  63. package/web-app/dist/assets/{trash-2-WYDISyed.js → trash-2-N1gXTHrf.js} +1 -1
  64. package/web-app/dist/assets/{trending-down-iUK45Jk7.js → trending-down-CfbZNev0.js} +1 -1
  65. package/web-app/dist/assets/{trending-up-Ca_zIVLF.js → trending-up-Dy5OS7pv.js} +1 -1
  66. package/web-app/dist/assets/{upload-DWDtE29t.js → upload-D-AYd3Lw.js} +1 -1
  67. package/web-app/dist/assets/{usePolling-DqMVwR9D.js → usePolling-C_MFEfgd.js} +1 -1
  68. package/web-app/dist/assets/{user-Bgt0jZl5.js → user-C8-BP2aP.js} +1 -1
  69. package/web-app/dist/index.html +1 -1
@@ -0,0 +1,399 @@
1
+ # Loki 10 Part 2: `loki modernize` design
2
+
3
+ Source: the D30 directive as dispatched on 2026-09-28 (PROGRESS.md 01:51Z entry). D30 has no row in DECISIONS.md yet. The moat order holds: nothing here weakens the Seal, the Wall, Rule of Two or honest verdicts. I read main at 677f88dd. Every file:line cited below comes from that tree.
4
+
5
+ ## 1. Goals and non-goals
6
+
7
+ **Goals**
8
+ - One command, `loki modernize <repo> --to "<target>"`, converts a codebase with no human steps and survives restarts.
9
+ - Every converted unit has a Wall made of behavior captured from the OLD code before any change.
10
+ - Claims stop exactly where equivalence data stops. Units without proof are listed as NOT PROVEN, with a reason.
11
+ - Cost, wall time and risk are printed before any model spend. `--budget` is a hard stop.
12
+ - First targets: Python 2 to 3, then Java 8 to 21. COBOL to Java and AngularJS to React come after the eval gates pass on the first two (section 6).
13
+
14
+ **Non-goals**
15
+ - No change to the legacy `loki migrate` (`autonomy/loki:17489`, `dashboard/migration_engine.py`). It is legacy engine code, `autonomy/loki` is on the no-edit list, and its phase gates are agent-judged rather than behavior-captured. It stays as it is.
16
+ - No model-judged equivalence. Equivalence is a deterministic diff over captured cases.
17
+ - No claim of "complete" for a codebase unless every unit is proven equivalent.
18
+ - No new always-on prompt text outside the cache-stable prefix.
19
+ - No flip of the engine default. `loki modernize` reaches engine10 whatever the default is (section 2).
20
+
21
+ ## 2. CLI and flags
22
+
23
+ ```
24
+ loki modernize <repo> --to "<target>" [--budget USD] [--workers N] [--remote URL]
25
+ [--resume <mid>] [--dry-run] [--provider P] [--no-pr]
26
+ ```
27
+
28
+ | Flag | Meaning |
29
+ |---|---|
30
+ | `--to` | Required. Resolves to a target module: `python3`, `java21`. Later: `java` from COBOL, `react`. An unknown target exits 2 and lists the supported ones. |
31
+ | `--budget` | USD hard cap for the whole modernization. Dispatch stops when spent plus the in-flight estimate would exceed it. |
32
+ | `--workers` | Concurrent unit workers. Default 4, max 32 locally. With `--remote`, the cap comes from the cluster. |
33
+ | `--resume <mid>` | Resumes from the modernization event log at unit level (section 5). |
34
+ | `--dry-run` | Runs Inventory only. Prints the unit count, wave plan and estimate, then exits 0 with zero model spend. |
35
+ | `--remote URL` | Dispatches units to POST /jobs on a cluster (section 3.4). |
36
+
37
+ **Routing**
38
+ - `bin/loki` gets one `modernize)` arm ahead of the `LOKI_ENGINE` block that execs `bun "$BUN_CLI" engine10 modernize "$@"`, so the legacy default tonight does not block it.
39
+ - The engine10 `cli.ts` TABLE gets `modernize: { module: "modernize/cli.ts", fn: "main" }`.
40
+ - Without bun, the command exits 1 with a one-line hint. It never falls back to `loki migrate`.
41
+
42
+ **Modernization id:** `mod-<utc>-<short>`. All state lives under `<repo>/.loki/modernize/<mid>/`.
43
+
44
+ ## 3. The pipeline
45
+
46
+ **Event log**
47
+ - Each modernization has one append-only log at `.loki/modernize/<mid>/events.jsonl`.
48
+ - It reuses `events.ts` (`EventLog`, `fold`, `tail`) and the supervisor's running-sha256 single-writer pattern (`supervisor.ts:84 SupervisorLog`).
49
+ - `validateEnvelope` (`events.ts:13`) accepts any non-empty type string, so the modernize event types live in `modernize/types.ts` and `engine10/types.ts` stays unedited.
50
+ - Each unit is an ordinary v10 run under `.loki/runs/<run-id>/`. The modernize log references it by run id.
51
+
52
+ ### 3.1 Inventory (deterministic, no model spend)
53
+
54
+ **Inputs:** the repo path and the target.
55
+
56
+ **Work**
57
+ - **Files:** `repomap.listRepoFiles` (`repomap.ts:29`, `git ls-files`), with no file cap in this mode.
58
+ - **Languages and build system:** detected by extension and marker files (`setup.py`, `pyproject.toml`, `tox.ini`, `pom.xml`, `build.gradle`).
59
+ - **Entry points:** console scripts, `__main__`, `main(String[])`, servlet or web.xml.
60
+ - **Existing tests:** `testmap.buildTestMap` (`testmap.ts:174`).
61
+ - **Dependency graph**
62
+ - Python: import extraction with the stdlib `tokenize`, which lexes Python 2 source (print statements included) where py3 `ast` cannot parse it.
63
+ - Java: `jdeps -verbose:class` over compiled classes, since jdeps ships with the JDK. If the build fails, fall back to scanning `import` lines and record the fallback in the inventory.
64
+ - **Clustering:** Tarjan SCC condensation, packing of nodes into units under the sizing rule (section 4), and topological layers of the unit DAG, which become the waves.
65
+ - **Risk score per unit:** size, fan-in, test coverage of the existing suite, and a legacy score (density of removed APIs: py2 `print`, `unicode`, `iteritems`; Java JAXB, `sun.*`, finalizers, `SecurityManager`).
66
+
67
+ **Prior art examined and not reused**
68
+ - `project_graph.ts` discovers cross-project app members, not a code dependency graph.
69
+ - `loki_graph_query` (`mcp/server.py:2531`) needs an external graphify build. When `graphify-out/` exists, Inventory reads its edges as a cross-check only and never depends on them.
70
+
71
+ **Outputs:** `inventory.json` (files, languages, build, entry points, tests, graph edges, units, waves) and `estimate.json`.
72
+
73
+ **Estimate**
74
+ - Cost: units x per-unit cost prior. The prior comes from the cache (`eta.ts` history, `cost.ts` records), else from the Legacy-Bench pilot median.
75
+ - Wall time: critical path of waves x p50 unit time / workers.
76
+ - Risk: each unit's risk class.
77
+ - The estimate prints before any spend. A missing prior prints "not measured", never $0.
78
+
79
+ **Events:** `modernize.started`, `inventory.completed`, `estimate.printed`.
80
+
81
+ **Failure handling:** an unparseable file becomes its own unit with a risk flag. It is not dropped. An undetectable build system stops the run with exit 2 before any spend.
82
+
83
+ ### 3.2 Oracle capture (before any change)
84
+
85
+ **Inputs:** the units, plus the OLD runtime (python2.7, or a JDK 8 toolchain).
86
+
87
+ **Old runtime:** its location is taken from `LOKI_MOD_OLD_RUNTIME`, or from PATH discovery. If it is missing, capture fails and every unit is NOT PROVEN "old runtime unavailable". Capture is never skipped.
88
+
89
+ **Work**
90
+ - Run the existing suite under the old runtime with branch coverage: coverage.py branch mode on 2.7, or the JaCoCo agent.
91
+ - Record I/O at unit boundaries (section 7).
92
+ - Run a coverage-guided input search. A read-only provider session sees the old unit's source and the uncovered branches, and proposes inputs. The harness executes them and keeps only the inputs that add branches. It stops at a plateau (two rounds with no gain) or at the unit's capture budget. This follows the witness-search result: 91.9% branch coverage with every generated case at parity (V10-RESEARCH section 7).
93
+ - Replay every case twice on the old runtime. A field that differs between the replays is marked nondeterministic.
94
+
95
+ **Seal**
96
+ - Each case file gets a sha256 and a sealed copy under `.loki/modernize/<mid>/oracle/<unit>/`, the same pattern as `wall.sealed`.
97
+ - About 20% of cases per unit are held out. The implementer never sees them, and they are checked only at verify (V10-RESEARCH section 1: visible oracles get inlined).
98
+
99
+ **Coverage floor**
100
+ - A unit whose captured branch coverage is below 80% gets one more capture round.
101
+ - If it is still below 80%, it is flagged `NOT PROVEN: coverage <x>% below 80%` in the up-front report and still converted. Its verdict can never exceed PARTIAL.
102
+
103
+ **Outputs:** `oracle/<unit>/cases.jsonl`, `oracle/<unit>/coverage.json`, `oracle/<unit>/sealed.json` (the hashes).
104
+
105
+ **Events:** `oracle.captured` (per unit: cases, held_out, branch_pct), `oracle.flagged`.
106
+
107
+ **Honest-verdict rule:** a boundary whose text or bytes type is undeclared, or a field that is nondeterministic with no declared normalizer, is NOT PROVEN for that field. It is never auto-masked.
108
+
109
+ ### 3.3 Plan in waves
110
+
111
+ **Inputs:** `inventory.json` and the oracle results.
112
+
113
+ **Work**
114
+ - Waves are topological layers, leaves first. A dependency cycle is one unit. It is never split across waves.
115
+ - Each unit card holds: unit id, files, entry symbols, target, dependencies, oracle path, risk, model route and caps.
116
+ - The target module adds the strangler adapter (section 8), so old and new coexist and every wave builds, runs and ships alone.
117
+
118
+ **Output:** `plan.json` plus `units/<unit>.md`, the brief each unit run receives. **Event:** `unit.planned`.
119
+
120
+ **Failure handling:** a wave that cannot build alone is merged into the next wave and the merge is recorded. The plan never ships a wave that needs a later one.
121
+
122
+ ### 3.4 Execute in parallel
123
+
124
+ **Unit runs**
125
+ - Each unit is one v10 engine run (`runSupervisor`, `supervisor.ts:174`) in its own git worktree. That keeps a single writer per unit (V10-RESEARCH section 4).
126
+ - The brief is the unit card. The Wall is the sealed oracle plus the target-conformance check (section 7).
127
+ - Deterministic codemods run first when installed: `python-modernize` or futurize for py2 to 3, OpenRewrite `UpgradeToJava21` for Java. The model fixes only what the codemods miss. A missing tool emits `codemod.skipped`, and the model does the whole unit.
128
+
129
+ **Model cascade**
130
+ - Sonnet first. Opus only on a failed Wall or failed test. Escalation is triggered by a failed check, never by self-assessment (V10-RESEARCH section 3).
131
+ - Units in the top legacy-risk class go straight to opus.
132
+ - A unit that fails once gets best-of-2: two fresh sessions from the same base. The first variant that is equivalent on all cases, held-out cases included, wins.
133
+
134
+ **Workers**
135
+ - Local mode: N workers under a coordinator in `modernize/scheduler.ts`. The coordinator is the modernize log's single writer.
136
+ - Remote mode:
137
+ - Units go to POST /jobs (`autonomy/trigger-server.py:13`) as `{"kind":"unit","card":...,"base":<sha>}`, bearer-token authenticated.
138
+ - The Helm `job-worker.yaml` gains a `worker.engine: v10` value, because today it runs `loki start`, which is the legacy engine.
139
+ - Workers return a patch plus a receipt through GET /jobs/<id>/proof and hold no push credential.
140
+ - The coordinator re-runs the equivalence check locally on every returned patch before accepting it. A remote receipt alone is never trusted (Rule of Two).
141
+
142
+ **Caps**
143
+ - Per-unit hard cap: the engine cap plus a cost cap from `estimate.json` x 3.
144
+ - On `--budget`, the coordinator stops dispatching, finishes in-flight units, and writes the report.
145
+
146
+ **Events:** `wave.started`, `unit.started` (run_id, model, worker), `unit.variant`, `budget.hit`.
147
+
148
+ ### 3.5 Verify
149
+
150
+ **Per unit**
151
+ - Differential equivalence: old against new on every captured case, held-out cases included (section 7).
152
+ - Target conformance.
153
+ - The no-op ablation from V10-RESEARCH section 1: replace the new unit with stubs, and the checker must go red. A checker that stays green marks the unit NOT PROVEN "oracle does not constrain unit".
154
+ - A captured-literal scan: new source that contains any held-out expected output verbatim is refused.
155
+
156
+ **Per wave:** full build and integration tests under the target runtime, plus the original suite.
157
+
158
+ **Fix and re-slice**
159
+ - A unit that is not equivalent after its fix rounds (`MAX_FIX_ROUNDS`) and best-of-2 is re-sliced smaller: the unit is re-clustered at function granularity, to a depth of 2.
160
+ - After that it is NOT PROVEN with the failing case ids. It is never force-passed.
161
+
162
+ **Events:** `unit.equivalence` (pass, fail, rate, branch_pct), `unit.resliced`, `unit.completed`, `unit.not_proven`, `wave.verified`.
163
+
164
+ ### 3.6 Ship
165
+
166
+ **PRs**
167
+ - One PR per wave through the existing credentialed push path (`stages/pr.ts`, `autonomy/lib/engine10-push.sh`), run only in the supervisor-side process.
168
+ - The PR body lists each unit: equivalence rate, coverage, NOT PROVEN reasons, run id and receipt sha256. Deep verify then runs on the PR as it does today.
169
+ - A wave PR is a draft unless every unit in it is proven.
170
+
171
+ **Routing and reversibility**
172
+ - Routing flags in `routes.json` switch traffic module by module (section 8).
173
+ - A wave stays reversible, with the old path kept, until all its units are proven equivalent. Only then does a later wave remove the old path.
174
+
175
+ **Events:** `wave.shipped` (pr url, draft), `route.switched`.
176
+
177
+ ### 3.7 Durable and observable
178
+
179
+ - **Resume:** `--resume <mid>` folds the log.
180
+ - Units with `unit.completed` or `unit.not_proven` are skipped.
181
+ - In-flight units resume through the existing `loki --resume <run-id>` (E-39).
182
+ - Units that never started are dispatched again.
183
+ - **Tamper:** a sha mismatch on the modernize log refuses every ship (`TAMPER_NOT_PROVEN`, `supervisor.ts:20`).
184
+ - **Budgets:** the global budget plus the per-unit hard caps.
185
+ - **Dashboard:** a new view on the engine10 dashboard (section 10).
186
+
187
+ ### 3.8 Final report
188
+
189
+ **Files:** `report.json` and `report.md` under `.loki/modernize/<mid>/`.
190
+
191
+ **Contents**
192
+ - Units converted.
193
+ - Proven equivalent, with coverage.
194
+ - NOT PROVEN, each with its exact reason.
195
+ - Total cost (null prints "not measured") and wall time.
196
+ - Human touches: a count of operator-input events. The target is 0.
197
+
198
+ **Verdict**
199
+ - COMPLETE only when every unit is proven.
200
+ - Otherwise PARTIAL, with the counts.
201
+ - FAILED when no wave shipped.
202
+
203
+ The report never states more than the equivalence data proves. It is sealed and signed through `stages/seal.ts` (`receipt_jwt.py`). UNSIGNED is stated when there is no key. **Event:** `modernize.completed`.
204
+
205
+ ## 4. Unit sizing rule
206
+
207
+ - **The horizon.** METR's 50% time horizon is about 320 minutes (Opus 4.5), and the 80% horizon is much shorter (V10-RESEARCH section 4).
208
+ - **The rule.** A unit must be convertible in well under the 80% horizon. The design sets the target at one quarter of the 50% horizon: 80 human-equivalent minutes per unit.
209
+ - **The deterministic proxy.** Inventory cannot measure minutes, so it uses a line cap. The starting value is 1,500 non-blank source lines per unit, with at most 40 files.
210
+ - This number is a starting heuristic, not a research result.
211
+ - The Legacy-Bench pilot (M-27) calibrates it: raise it while first-pass equivalence stays at 90% or higher, lower it when it does not.
212
+ - **Cycles and re-slicing.** An SCC larger than the cap stays one unit, flagged high risk, and routed to opus. Re-slicing (3.5) is the release valve.
213
+ - **Why parallel works here.** Parallelism gains only on decomposable work (+81%) and loses 39-70% on sequential work. Units in the same wave are independent by construction, and the waves themselves run in sequence.
214
+
215
+ ## 5. Durability and resume
216
+
217
+ - **State:** everything is on disk under `.loki/modernize/<mid>/`: `events.jsonl`, `inventory.json`, `plan.json`, `oracle/`, `units/`, `routes.json`, `report.*`.
218
+ - **Writes:** atomic (a temp file, then rename), the same as `writeEngineMarker` (`supervisor.ts:65`).
219
+ - **Recovery:** a crash at any point is recovered by folding the log. No step depends on in-memory state that is not in the log.
220
+ - **Worktrees:** unit worktrees live under `.loki/modernize/<mid>/wt/<unit>`. Resume reattaches a worktree when its HEAD matches the recorded base, and otherwise recreates it from the recorded base.
221
+ - **The resume test** (M-22) kills the coordinator mid-wave. It asserts that no unit runs twice to completion and that seq keeps increasing.
222
+
223
+ ## 6. Why Python 2 to 3 and Java 8 to 21 first, and COBOL later
224
+
225
+ **Python 2 to 3**
226
+ - Both interpreters run locally.
227
+ - coverage.py supports branch mode on 2.7.
228
+ - The 2/3-compatible intermediate form lets old and new coexist in one tree.
229
+ - Codemods exist.
230
+
231
+ **Java 8 to 21**
232
+ - JDK 21 runs Java 8 bytecode, so modules at different levels coexist in one JVM.
233
+ - JaCoCo, jdeps, jdeprscan and OpenRewrite are mature open source.
234
+ - Randoop generates regression tests, which feed the input search.
235
+
236
+ **COBOL to Java**
237
+ - It needs a GnuCOBOL or mainframe runtime and file or copybook I/O capture. It also has no in-process boundary, and Legacy-Bench scores run 16.9-42.5% (V10-RESEARCH section 7).
238
+ - The research gives a method (witness search, symbolic execution plus delta debugging) but no tooling we have.
239
+ - It starts after the first two targets meet the section 12 gates.
240
+
241
+ **AngularJS to React** follows COBOL. It needs browser-level capture (Playwright traces) as the oracle.
242
+
243
+ ## 7. Oracle capture per language family, and the equivalence checker contract
244
+
245
+ **Python 2 to 3 capture** (`autonomy/lib/modernize/py_capture.py`, runs on 2.7 and 3.x)
246
+ - A tracer wraps each unit's public entry symbols and records arguments, return value, raised exception type and message, stdout, and file writes under a temp root.
247
+ - **Serialization is type-tagged canonical JSON, never pickle.** A py2 `str` crossing into py3 is ambiguous, and pickle makes cases falsely equal or falsely different. Tags: `{"t":"bytes","v":<base64>}`, `{"t":"text","v":...}`, `{"t":"int"}`, `{"t":"float","v":...}`, `list`, `dict` (sorted keys), `none`, `exc`.
248
+ - **Boundary declaration.** Each unit declares text or bytes per boundary in its card. Inventory proposes a declaration from literal and codec usage. An undeclared boundary is NOT PROVEN.
249
+
250
+ **Java 8 to 21 capture** (`autonomy/lib/modernize/java_capture.sh` plus `modernize/oracle/java.ts`)
251
+ - Run the existing JUnit suite under JDK 8 with the JaCoCo agent. Add Randoop-generated regression tests per unit class, filtered to deterministic ones by the double replay.
252
+ - Cases record the method, serialized arguments (JSON through reflection, primitives, strings and collections only), the return value, the exception class and stdout.
253
+ - A unit whose boundary types are not serializable is NOT PROVEN "boundary not capturable". The run does not guess.
254
+
255
+ **Equivalence checker contract** (`modernize/equiv.ts`)
256
+
257
+ | Part | Detail |
258
+ |---|---|
259
+ | Input | The unit id, the sealed `cases.jsonl` (sha-verified before use), the new tree, and the target runner. |
260
+ | Normalizers | Declared per unit in the card and sealed with the oracle: float tolerance, set ordering, and named nondeterministic fields. Nothing is implicit. |
261
+ | Output | `{unit, cases, pass, fail, held_out_pass, held_out_fail, rate, branch_pct, failures:[{case, field, old, new}] (max 20), not_proven:[...]}` |
262
+ | Proven equivalent | rate = 100%, held-out rate = 100%, branch_pct of 80% or higher, the no-op ablation goes red, and conformance passes. |
263
+ | Otherwise | PARTIAL with the numbers, or FAILED when rate < 100% after re-slice. |
264
+
265
+ **Target conformance**
266
+ - The captured oracle passes on the OLD code by construction. The Wall therefore also carries a check that the old code fails:
267
+ - py3: the unit imports and runs its cases under python3;
268
+ - java21: it compiles with `--release 21` and `jdeprscan --for-removal --release 21` is clean.
269
+ - The Wall runs under the target runtime. A Wall that is green before any change is a design error reported as `oracle.flagged`. It is never ALREADY_SATISFIED.
270
+ - `stages/wall.ts` gains a pre-sealed mode (M-14) that takes these files instead of running the author session.
271
+
272
+ ## 8. Strangler adapters and routing flags per target
273
+
274
+ | Target | Coexistence pattern | Routing flag |
275
+ |---|---|---|
276
+ | Python 2 to 3 | Waves convert units to 2/3-compatible form (`__future__`, `six`), so the tree runs on both interpreters. The final wave drops 2.7 compatibility. | `routes.json` unit -> `py2`, `dual` or `py3`. The generated `tox.ini` envs run the suite on each interpreter a unit claims. |
277
+ | Java 8 to 21 | Each Maven or Gradle module moves its `release` level independently. JDK 21 loads the modules still at 8. Removed-API shims (JAXB and so on) come in as explicit dependencies. | `routes.json` module -> `8` or `21`. The target module writes the per-module compiler release into the build file. |
278
+ | COBOL to Java (later) | A CALL-boundary adapter: a batch step switch or program alias, so old and new programs share files. | The step-level switch in the job definition. |
279
+ | AngularJS to React (later) | `react2angular` wrappers per component, with route-level cutover. | Per-route flag. |
280
+
281
+ - Every switch emits `route.switched`.
282
+ - Switching back is always allowed until the wave is fully proven.
283
+
284
+ ## 9. Wave shipping
285
+
286
+ 1. Wave verify passes.
287
+ 2. One PR is opened, with a table of unit receipts.
288
+ 3. Deep verify runs.
289
+ 4. Routes switch for the proven units only.
290
+ 5. The next wave starts from the merged tree.
291
+
292
+ - Waves ship even when some units are NOT PROVEN. Those units keep the old route, and the PR is a draft that names them.
293
+ - `--no-pr` keeps everything local for evals.
294
+
295
+ ## 10. Dashboard view
296
+
297
+ - A `/modernize/<mid>` page on the existing 127.0.0.1-only engine10 dashboard (`dashboard/server.ts`), folded from the modernize log with `fold`/`tail`.
298
+ - It shows:
299
+ - units done/total;
300
+ - equivalence % (proven / converted);
301
+ - coverage % (the median branch_pct and the count below 80%);
302
+ - cost so far ("not measured" when null);
303
+ - ETA (`eta.ts`);
304
+ - current failures by cause, grouped as runtime missing, coverage low, not equivalent, conformance, cap, and budget.
305
+ - It never opens a browser.
306
+
307
+ ## 11. Risks
308
+
309
+ | Risk | Mitigation |
310
+ |---|---|
311
+ | The implementer inlines captured outputs | Held-out cases, read-only sealed oracle, no-op ablation, captured-literal scan (3.5). |
312
+ | The oracle under-constrains behavior | 80% branch floor, NOT PROVEN below it, witness search. The report never rounds up. |
313
+ | Text and bytes confusion in py2 to 3 | Type-tagged JSON and per-boundary declarations (section 7). |
314
+ | Nondeterministic legacy code | Double replay and declared normalizers only. Unexplained drift is NOT PROVEN. |
315
+ | The line-cap proxy is wrong | Calibrated on the pilot, plus re-slicing. |
316
+ | Remote worker compromise | Workers hold no push token, and the coordinator re-verifies every patch locally. |
317
+ | Cost blowup at 1M lines | Estimate before spend, `--budget`, per-unit caps, sonnet first. |
318
+ | The old runtime is unavailable (2.7 is end of life) | The unit is NOT PROVEN, never skipped. Documented container images supply python2.7 and JDK 8. |
319
+ | File contention with in-flight engine rows | Dependencies on E-31, E-54 and E-39 (section 13). |
320
+
321
+ ## 12. Eval plan (publish everything, failures included)
322
+
323
+ **Metrics, per run:** equivalence rate, NOT PROVEN share, human touches, cost per 1K source lines, wall time, units re-sliced.
324
+
325
+ **1. Legacy-Bench pilot (M-27)**
326
+ - V10-RESEARCH cites Legacy-Bench only through a vendor report.
327
+ - The slice first confirms that a public task set exists and records its URL and license. If none exists, the pilot uses 10 public py2 and Java 8 repos under 20K lines, each with a suite, and says so.
328
+ - Gate: the equivalence rate on the pilot is published, and the sizing cap is calibrated from it.
329
+
330
+ **2. 200K-line scale test (M-28)**
331
+ - Chosen candidate: Mercurial at its last Python 2 only release, 4.x (py2 to 3).
332
+ - Criteria:
333
+ - at least 200K source lines excluding vendored code;
334
+ - a suite that runs headless in bounded time on the old runtime;
335
+ - an upstream human port exists as a reference to diff against;
336
+ - the license (GPL) allows running it.
337
+ - Why: it is a real Python 2 codebase with a large suite of its own (`run-tests.py`), and upstream later completed the Python 3 port, so a human reference exists.
338
+ - The line count is UNVERIFIED offline. M-28 measures it first with `--dry-run` inventory and `cloc`. If the count is under 200K or the suite is unbounded, the named fallback is Calibre before its Python 3 port, and the choice is recorded again.
339
+
340
+ **3. 1M lines (M-29)**
341
+ - Chosen candidate: Apache Hadoop 2.x (Java 8 to 21). It is Java 8, multi-module, and has an extensive JUnit suite.
342
+ - The line count is UNVERIFIED. It is measured the same way, with its suite run per module under the wave plan.
343
+
344
+ **Publishing:** results go to `eval/modernize/results/` and a summary to `docs/v10/METRICS.md` through the EV owner. That includes every NOT PROVEN unit and every failure.
345
+
346
+ ## 13. Slice plan (M-01..M-30)
347
+
348
+ **Paths**
349
+ - `mod/` means `loki-ts/src/engine10/modernize/`.
350
+ - `T/` means `loki-ts/tests/engine10/modernize/`.
351
+ - `Wb` means `cd loki-ts && bun test`.
352
+
353
+ **Merge-time exception.** New shell tests register in `tests/run-all-tests.sh` and `tests/shard-durations.tsv`, and new modules register in `engine10/registry.ts`. The Release Manager adds those one-line registrations at merge, as with the E-32 registry lines, so no slice owns those three files.
354
+
355
+ **Shared-file dependencies**
356
+ - M-07 and M-08 touch `engine10/cli.ts` and `bin/loki`. E-31 owns both (approved, held until 03:00Z), so M-07 and M-08 depend on E-31 merging or parking. They never depend on the flip itself.
357
+ - M-14 touches `stages/wall.ts` and depends on E-54 (building).
358
+
359
+ | ID | Goal | Files | Tier | Wall check | Deps |
360
+ |---|---|---|---|---|---|
361
+ | M-01 | Modernize event types, log wrapper and state paths | mod/types.ts, mod/log.ts, T/log.test.ts | MEDIUM | Wb T/log.test.ts | none |
362
+ | M-02 | Inventory core: files, languages, build, entry points, tests | mod/inventory.ts, T/inventory.test.ts, T/fixtures/inv/ | MEDIUM | Wb T/inventory.test.ts | M-01 |
363
+ | M-03 | Python import graph via tokenize (lexes py2 source) | mod/lang/python.ts, autonomy/lib/modernize/py_imports.py, T/python_graph.test.ts | MEDIUM | Wb T/python_graph.test.ts | M-02 |
364
+ | M-04 | Java graph via jdeps, import-scan fallback recorded | mod/lang/java.ts, T/java_graph.test.ts, T/fixtures/java8/ | MEDIUM | Wb T/java_graph.test.ts | M-02 |
365
+ | M-05 | SCC clustering, 1,500-line unit cap, topological waves | mod/cluster.ts, T/cluster.test.ts | MEDIUM | Wb T/cluster.test.ts | M-02 |
366
+ | M-06 | Up-front estimate (cost, time, risk), --budget math, --dry-run print | mod/estimate.ts, T/estimate.test.ts | LOW | Wb T/estimate.test.ts | M-05 |
367
+ | M-07 | modernize CLI and flag parsing; TABLE route in engine10 cli | mod/cli.ts, loki-ts/src/engine10/cli.ts (TABLE and USAGE line only), T/cli.test.ts | MEDIUM | Wb T/cli.test.ts | M-06, E-31 merged or parked |
368
+ | M-08 | bin/loki modernize arm to engine10 regardless of default | bin/loki (modernize arm only), tests/test-modernize-dispatch.sh | MEDIUM | bash tests/test-modernize-dispatch.sh | M-07 |
369
+ | M-09 | py2/3 capture tracer with type-tagged JSON and branch coverage | autonomy/lib/modernize/py_capture.py, tests/test-modernize-py-capture.sh, tests/fixtures/modernize/py2/ | HIGH | bash tests/test-modernize-py-capture.sh | M-01 |
370
+ | M-10 | Coverage-guided input search (read-only session, plateau stop) | mod/oracle/search.ts, T/search.test.ts | HIGH | Wb T/search.test.ts | M-09 |
371
+ | M-11 | Java capture: JUnit under JDK 8 with JaCoCo plus Randoop | autonomy/lib/modernize/java_capture.sh, mod/oracle/java.ts, T/java_capture.test.ts | HIGH | Wb T/java_capture.test.ts | M-04 |
372
+ | M-12 | Oracle seal, held-out split, 80% floor, up-front NOT PROVEN | mod/oracle/seal.ts, T/oracle_seal.test.ts | HIGH | Wb T/oracle_seal.test.ts | M-09 |
373
+ | M-13 | Equivalence checker with sealed normalizers and contract output | mod/equiv.ts, T/equiv.test.ts, T/fixtures/equiv/ | HIGH | Wb T/equiv.test.ts | M-12 |
374
+ | M-14 | Wall pre-sealed mode: oracle plus conformance, green base is an error | loki-ts/src/engine10/stages/wall.ts (pre-sealed branch only), T/wall_presealed.test.ts | HIGH | Wb T/wall_presealed.test.ts | M-12, E-54 |
375
+ | M-15 | Unit runner: card to v10 run with oracle Wall and equiv check | mod/unit.ts, T/unit.test.ts | HIGH | Wb T/unit.test.ts | M-13, M-14 |
376
+ | M-16 | Deterministic codemods first (futurize, OpenRewrite) or skipped | mod/codemod.ts, T/codemod.test.ts | MEDIUM | Wb T/codemod.test.ts | M-02 |
377
+ | M-17 | Scheduler: N workers, worktrees, cascade, best-of-2, budget stop | mod/scheduler.ts, T/scheduler.test.ts | HIGH | Wb T/scheduler.test.ts | M-15 |
378
+ | M-18 | Re-slice non-equivalent units to depth 2, then NOT PROVEN | mod/reslice.ts, T/reslice.test.ts | MEDIUM | Wb T/reslice.test.ts | M-05, M-13 |
379
+ | M-19 | Strangler targets and routes.json (py2/dual/py3, per-module release) | mod/routes.ts, mod/targets/python3.ts, mod/targets/java21.ts, T/routes.test.ts | MEDIUM | Wb T/routes.test.ts | M-05 |
380
+ | M-20 | Wave verify: full build, integration and original suite | mod/wave.ts, T/wave.test.ts | MEDIUM | Wb T/wave.test.ts | M-17, M-19 |
381
+ | M-21 | Wave ship: one PR per wave with unit receipts, draft unless all proven | mod/ship.ts, T/ship.test.ts | HIGH | Wb T/ship.test.ts | M-20 |
382
+ | M-22 | Unit-level resume after coordinator crash | mod/resume.ts, T/resume.test.ts | MEDIUM | Wb T/resume.test.ts | M-17, E-39 |
383
+ | M-23 | Remote units via POST /jobs kind unit, Helm worker.engine v10, local re-verify | autonomy/trigger-server.py (unit job kind only), deploy/helm/autonomi/templates/job-worker.yaml, deploy/helm/autonomi/values.yaml, mod/remote.ts, T/remote.test.ts, tests/test-trigger-server-unit-jobs.py | HIGH | python3 tests/test-trigger-server-unit-jobs.py && Wb T/remote.test.ts | M-17 |
384
+ | M-24 | Dashboard /modernize view | loki-ts/src/engine10/dashboard/modernize.ts, loki-ts/src/engine10/dashboard/server.ts (one route), T/dashboard.test.ts | LOW | Wb T/dashboard.test.ts | M-01 |
385
+ | M-25 | Final report and verdict (COMPLETE only if all proven), sealed | mod/report.ts, T/report.test.ts | HIGH | Wb T/report.test.ts | M-13, M-21 |
386
+ | M-26 | End-to-end: py2 fixture repo modernized with stub provider | T/e2e.test.ts, T/fixtures/e2e_py2/ | MEDIUM | Wb T/e2e.test.ts | M-08, M-22, M-25 |
387
+ | M-27 | Legacy-Bench pilot harness; confirm public set; calibrate cap | eval/modernize/pilot.py, eval/modernize/test-pilot.sh | HIGH | bash eval/modernize/test-pilot.sh | M-26 |
388
+ | M-28 | 200K scale run (Mercurial 4.x, measured first, fallback Calibre) | eval/modernize/scale.py, eval/modernize/SCALE.md | HIGH | python3 eval/modernize/scale.py --target hg --dry-run-first | M-27 |
389
+ | M-29 | 1M scale run (Hadoop 2.x Java 8 to 21, measured first) | eval/modernize/scale_1m.py, eval/modernize/SCALE-1M.md | HIGH | python3 eval/modernize/scale_1m.py --dry-run-first | M-28, M-11 |
390
+ | M-30 | User docs for loki modernize, no dashes, commands match USAGE | docs/v10/GUIDE-MODERNIZE.md, tests/test-modernize-docs.sh | LOW | bash tests/test-modernize-docs.sh | M-07 |
391
+
392
+ **Critical path:** M-01 -> M-02 -> M-05 -> M-09 -> M-12 -> M-13 -> M-14 -> M-15 -> M-17 -> M-20 -> M-21 -> M-25 -> M-26 -> M-27 -> M-28.
393
+
394
+ **Parallel from the start:** M-03, M-04, M-06, M-16, M-19 and M-24 run alongside it.
395
+
396
+ **Red and green for every slice**
397
+ - Red: the test file is missing or asserts the absent behavior.
398
+ - Green: every assertion named in the goal holds.
399
+ - Each HIGH slice needs a unanimous opus review (D12).
@@ -1,5 +1,10 @@
1
1
  # Progress
2
2
 
3
+ ## 2026-09-28T03:44Z: RELEASE BLOCKED -- read first on resume
4
+ - v10.0.0 did NOT publish. The release commit 898fa081 (tag v10.0.0 pushed) failed Tests shards 1/8 and 5/8, so the Release workflow failed; `npm view loki-mode version` is still 9.81.0. Failing suites (gh run view 36372016155 --log-failed): "loki start surfaces a stale install" (line: "FAIL: a stale install prints NO warning on start") and "trust-core tests detect their regressions".
5
+ - Next step: fix those 2 shards on main (likely the 10.0.0 major bump changes the version comparison the stale-install check uses; confirm from the test before editing), wait for Tests green on the fix SHA, then re-release v10.0.0 (the tag v10.0.0 already points at 898fa081: delete nothing; cut the re-release per docs/dev/release-checklist.md, bumping to 10.0.1 if the tag cannot be reused). Default stays legacy; v10 opt-in; gate numbers are in CHANGELOG v10.0.0 and docs/v10/METRICS.md.
6
+ - Session hand-off: no workflows started after 03:11Z. Merge-ready (approved, not merged): E-68, E-69, EV-13 (branches worktree-wf_b707f4ab-24f-3/-4/-5), E-64 (worktree-wf_4601e7b3-4d8-1), E-70 already merged, E-31 held for the gate. Needs rework: E-66 (small test fixes), E-67 (backstop inside the cap), M-09, M-01/02/05/06 (iterative SCC), EV-11 (11 medium tasks missing), EV-12 (REJECT: large tasks smaller than medium). Both workflows (p0-rework, d30-rework-and-m1) finished; nothing running.
7
+
3
8
  ## Current
4
9
 
5
10
  - Milestone: **M0 measure first**, item 1: the moat suite.
@@ -1289,3 +1294,24 @@ sample.
1289
1294
  - MODERNIZE.md: CTO and Architect dispatched 01:52Z; ETA 02:45Z for the design doc, Part 2 slices cut by the PO right after.
1290
1295
  - CI: main red at d00c5ede and 7c3dea3d from train E13 (providers.test.ts dies where claude is absent because preflight exits in-process; test-engine10-dispatch.sh expected 2 cli.ts lines, E-32 made it 3). Both fixes are in train E14 (404 pass, 0 fail locally, plus a no-claude PATH run); push after the remaining no-claude check.
1291
1296
  - Top blocker: cost. v10 at $0.40 per completed task vs the $0.118 target.
1297
+
1298
+ ## 2026-09-28T02:27Z: Loki 10 progress
1299
+ - Part 1 gate numbers (small tier). Raw claude -p, claude-opus-5-5, 29 tasks: 27/29 (93.1%), $0.2363 per completed, p50 39s. Legacy (finished): 15/29 completed, 26/29 hidden pass, p50 190s, cost not measured. v10 full arm: not run yet.
1300
+ - Cost root cause found (E-65, built 92e3c06c, in rework after an opus CONCERN): every v10 session carried a 20.4k-token fixed prefix (25 tools plus the legacy autonomy append; on real hosts also the operator's CLAUDE.md, memory and skills). Lean engine sessions: 7.1k. Same 5 tasks, claude-sonnet-5: v10 4/5 at $0.1523 per completed vs raw claude -p 4/5 at $0.2113; one v10 implement session $0.093 vs raw $0.167 to $0.180 on pub-more-itertools-1252. Target $0.118: not met yet; E-64 (lean small path plus opus-on-failure) is next.
1301
+ - CI: main green at 5815a2f0 (Tests, Coverage, Bun Parity success) after the preflight fix (5df685f4: preflight raises in-process; suites pass with no claude CLI, no gh auth, no git identity: engine10 402 pass 0 fail). v9.81.0 tagged at 028bd5dc and publishing.
1302
+ - Part 2: MODERNIZE.md in progress (CTO and Architect), ETA 02:45Z; 0 Part 2 slices done.
1303
+ - In flight: E-64, E-63 (STALE_PROGRESS), EV-11 (medium tier), EV-12 (large tier), E-66..E-70 and EV-13 (augmentiq #52 P0), E-65 rework.
1304
+ - Top blocker: v10 cost per completed task vs the $0.118 target, then the medium and large tiers (not built yet) for the accuracy claim.
1305
+
1306
+ ## 2026-09-28T02:57Z: Loki 10 progress
1307
+ - Part 1 gate numbers (small tier, claude-opus-5-5, 29 tasks): raw 27/29 (93.1%), $0.2363, p50 39s; v10 default knobs 26/29 (89.7%), $0.3946 (2 unmeasured), p50 85.5s (~/loki-ci-logs/ev9-v10-small/results.jsonl); legacy 15/29, cost not measured. Gate not met; v10.0.0 ships opt-in with these numbers; default stays legacy (D30).
1308
+ - Lean configuration (no plan, no Wall; not the default), full 29 tasks: 27/29 (93.1%), $0.1616 per completed (1 unmeasured), p50 40s, p90 67s (~/loki-ci-logs/ev9-v10-small-lean/results.jsonl). Matches raw on completion and time, 32% cheaper; short of the 2x targets.
1309
+ - Part 2: MODERNIZE.md merged in train E17 (8f4dc965, 399 lines, M-01..M-30 on the board); first builders on M-01/M-02/M-05/M-06 and M-09. 0 Part 2 slices merged.
1310
+ - Merged this half hour: E-65 lean engine sessions (8b76fd9a), E-63 STALE_PROGRESS pulse check, E-70 dashboard versions (train E17). Released v9.81.0 (npm 02:38:54Z).
1311
+ - Top blocker: default v10 is heavier than raw (plan and Wall on every normal task, $0.39 vs $0.24). E-64 (lean small path plus opus on failure) is the fix; it was rejected once and is in rework.
1312
+
1313
+ ## 2026-09-28T03:44Z: Loki 10 progress
1314
+ - Part 1 gate numbers unchanged from 02:57Z (small tier, claude-opus-5-5): raw 27/29, $0.2363, p50 39s; v10 default 26/29, $0.3946, p50 85.5s; v10 lean configuration 27/29, $0.1616, p50 40s (~/loki-ci-logs/ev9-v10-small{,-lean}/results.jsonl). Gate not met; v10.0.0 release failed (see the top of this file).
1315
+ - P0 augmentiq #52 rework: E-68, E-69, EV-13 APPROVE; E-66 CONCERN; E-67 REJECT. E-64 (lean small path plus opus on failure) APPROVE on rework.
1316
+ - Part 2: 0 slices merged. M-01, M-02, M-05, M-06 built (CONCERN: recursive SCC); M-09 built (REJECT: unsupported types recorded as equal).
1317
+ - Top blocker: the 2 red Tests shards on 898fa081 block the v10.0.0 release.