|
3 | 3 |
|
4 | 4 | # NEXT.md |
5 | 5 |
|
6 | | -> **Test-count floor, 2026-08-20 — 17,676 / 38, AND THE GATE IS GREEN.** The complete |
7 | | -> 38-executable gate reads **17,676 run: 17,676 passed, 0 failed, 0 skipped**, recounted from the |
| 6 | +> **Test-count floor, 2026-08-20 — 17,732 / 38, AND THE GATE IS GREEN.** The complete |
| 7 | +> 38-executable gate reads **17,732 run: 17,732 passed, 0 failed, 0 skipped**, recounted from the |
8 | 8 | > per-executable logs with every executable run separately and continuing past failures, zero build |
9 | | -> warnings at `--parallel 2`. Every checkpoint below this one ends with *"the gate is not green"*; |
10 | | -> this one does not. Two of the three historical failure sources were environmental and are simply |
11 | | -> absent in this container; the third was a real repair (#2351). |
| 9 | +> warnings at `--parallel 2`. Graph **41 / 95**, seams **3 / 20**, negative fixtures **53 / 269**. |
12 | 10 | > |
13 | | -> **The queues were empty and the work was found by measurement instead (#2397, then #2398).** |
14 | | -> With `ticket` at 0 `todo` and `task` fully classified, the next move was **not** to stop but to |
15 | | -> point the restored reference at the modules with **no test sources of their own**. Two have now |
16 | | -> been measured and both held real defects. |
| 11 | +> --- |
17 | 12 | > |
18 | | -> **#2397 — `System::Text::RegularExpressions`.** Four divergences, **two of them silent data loss |
19 | | -> through a public member**: `Regex::Split` discarded every matched capture group's value *and* |
20 | | -> dropped a trailing empty segment, both because the body was a single |
21 | | -> `std::sregex_token_iterator(-1)`. |
| 13 | +> ## THE POST-AUDIT QUEUE IS EMPTY |
22 | 14 | > |
23 | | -> **#2398 — `System::Security::Cryptography::RandomNumberGenerator`.** It **threw |
24 | | -> `PlatformNotSupportedException` on Emscripten**, on a premise **this repository had already |
25 | | -> measured false**: `Guid.cpp:377-388` records #2228's finding that Emscripten's `getentropy()` is |
26 | | -> `__wasi_random_get()`, so `Guid::NewGuid()` has been getting real entropy there through the very |
27 | | -> call the cryptographic RNG refused to make. .NET does not refuse either. **And its shipped tests |
28 | | -> could not have caught it** — two cases asserting `buffer.size()` after filling a buffer whose size |
29 | | -> was fixed before the call, both of which pass against a generator that writes nothing. |
| 15 | +> Measured 2026-08-20: `ticket` is **2,406 done, 0 todo, 3 blocked, 5 wontfix**, and `task` is fully |
| 16 | +> classified (1,082 ported, 15,119 ignored). **There is no remaining implementation work that this |
| 17 | +> container can do.** |
30 | 18 | > |
31 | | -> **#2401 — `ClientWebSocket`'s entropy.** Found by asking #2398's question **once more** rather |
32 | | -> than by opening another module: *#2228 put a real CSPRNG behind `Guid::NewGuid` — what else needs |
33 | | -> unpredictable bytes, and where does it get them?* `ClientWebSocket` drew **both** its |
34 | | -> `Sec-WebSocket-Key` nonce and its **per-frame masking key** from `std::random_device`, which the |
35 | | -> standard permits to be deterministic and which **`Random.cpp:69-70` already records as |
36 | | -> deterministic on MinGW-w64**, a supported target. RFC 6455 §5.3 requires the mask to come from a |
37 | | -> strong source of entropy by name. |
| 19 | +> **The three blocked tickets are not unfinished work; they are work this container lacks the means |
| 20 | +> to do**, and each was measured rather than assumed: |
38 | 21 | > |
39 | | -> **#2402 closed the entropy sweep by recording what is NOT a defect** — the half a sweep usually |
40 | | -> leaves out. Five sites need random bytes; two were repaired and **two are parity and look exactly |
41 | | -> like the defect**, because `HashCode::GlobalSeed` and unseeded `Random` both call |
42 | | -> `std::random_device`. .NET has **two** entropy entry points and deliberately uses the |
43 | | -> **non**-cryptographic one at both. `docs/EntropySourceSweep.md` records all five with citations, |
44 | | -> so nobody re-opens them. |
| 22 | +> * **#1773** and **#2381** wait on the `next` → `develop` merge. #2381 is the sharper of the two: |
| 23 | +> `cna` builds against the **sibling checkout on `develop`**, where `DateTimeKind` does not exist |
| 24 | +> at all, so the repair cannot compile before the merge — not a decision, an impossibility. |
| 25 | +> * **#1962** needs a raw ICMP socket, hence `CAP_NET_RAW`, which this container does not have. |
45 | 26 | > |
46 | | -> **Three patterns worth carrying forward, in order of yield.** (1) **Ask a repaired subsystem's |
47 | | -> question of its neighbours** — that alone found #2398 and #2401, and #2402 closed it out. (2) **A |
48 | | -> module with no test sources** is where an undetected divergence survives. (3) **A test that cannot |
49 | | -> fail** is how it survives there: **every** defect this sweep found sat under one, **four** of them |
50 | | -> literally — two `EXPECT_EQ(buffer.size(), N)` on a buffer sized before the call, a case whose name |
51 | | -> claimed "DiffersAcrossProcesses" while its body only checked within one, and |
52 | | -> `EXPECT_NO_THROW(System::ComponentModel::Attribute{})`, which asserted that an empty type can be |
53 | | -> default-constructed. §4b has the remaining candidates. |
| 27 | +> The five `wontfix` entries are recorded decisions with their reasons, not oversights. |
54 | 28 | > |
55 | | -> **#2403 — `System::ComponentModel`, and the pattern that found it is pattern (2) below.** Six |
56 | | -> attributes published a **bare mutable public data member** where .NET publishes a get-only |
57 | | -> property, most with **no statics and no equality members at all** — while four siblings **in the |
58 | | -> same header** already had the correct shape. The module's whole prior coverage for those six was |
59 | | -> constructor round-trips through the public field, so .NET's `Default` values — the actual contract |
60 | | -> of a metadata attribute — were unpinned, and `MergablePropertyAttribute::Default` is `Yes` where |
61 | | -> its four siblings are `No`. |
| 29 | +> --- |
62 | 30 | > |
63 | | -> **#2405 finished the `component-model` pass**: the two `PropertyChanged`/`PropertyChanging` |
64 | | -> event-args types each carried a **second, mutable, lossy** copy of `PropertyName` beside the |
65 | | -> private `std::optional` — a field whose own doc-comment described the loss and kept it "for |
66 | | -> existing consumers", a reason that measures to **zero** — and `System::ComponentModel::Attribute` |
67 | | -> turned out to be a **phantom**: no .NET counterpart, no members, no derived classes, no callers. |
| 31 | +> ## HOW THE LAST STRETCH WENT, AND WHAT IS WORTH CARRYING FORWARD |
68 | 32 | > |
69 | | -> **#2406 closed the `component-model` sweep, and its largest half is a DECLARATION.** The eleven |
70 | | -> `ValidationAttribute` subclasses — `Required`, `Range`, `StringLength`, `RegularExpression`, |
71 | | -> `EmailAddress` and the rest — **validate nothing**; .NET has `IsValid`/`Validate`/ |
72 | | -> `FormatErrorMessage`/`RequiresValidationContext` and this port has none of them. The **names** are |
73 | | -> what make that dangerous: a caller who writes `RequiredAttribute` and sets an error message has |
74 | | -> every reason to think something checks it, and the mistake surfaces as *validation that silently |
75 | | -> never happened*. It is now a `@warning` on the base class and a pinned absence — **mutation M7 |
76 | | -> adds an `IsValid` and is caught**, so the declaration is enforced. |
| 33 | +> The queue was emptied by the date/time chain rooted at **#1940**, plus three findings made **while |
| 34 | +> working on something else**. That second category is the one worth naming, because none of the |
| 35 | +> three was on any list: |
77 | 36 | > |
78 | | -> **SA-14 WAS GRANTED ON 2026-08-20 AND UNBLOCKS THE DATE/TIME CHAIN.** Three decisions, all as |
79 | | -> recommended, recorded in `docs/StandingApprovals.md`: (1) the provider reaches the parser by |
80 | | -> **moving `DateTimeFormatInfo` into `Core.Base`** — measured at two files and **zero changed include |
81 | | -> lines**, against 34 files across eight modules for the new-component shape #1940's own wording |
82 | | -> implied; (2) the **culture-concurrency** defect is repaired **separately and first** — that is |
83 | | -> **#2409, now done**; (3) an unrecognised culture **name** throws from **both** doors. |
| 37 | +> * **#2415 — a gate had been RED for a day behind a green test count.** |
| 38 | +> `scripts/check_selective_components.sh` failed on a fixture #1889 had legitimately invalidated, |
| 39 | +> **and nothing ran the script**, so CLAUDE.md rule 2's green reading said nothing about it. It |
| 40 | +> also **built eight trees into `/tmp`**, the one place the build policy exists to keep builds out |
| 41 | +> of — a mechanism (`build-tmp/`) that was designed and never wired up. It now runs last in |
| 42 | +> `local_ci_check.sh`, at a measured ~10 minutes, deliberately **not** behind an opt-out: *a check |
| 43 | +> that can be skipped is the check that rotted.* |
| 44 | +> * **#2416 — the two halves of one type disagreed about what `o` means.** A probe taken to check |
| 45 | +> something else measured that `DateTime::ToString` had **no standard-format table at all**: |
| 46 | +> `ToString("o")` emitted the literal `"o"`, `"s"` returned `"0"` by reading it as *seconds*. The |
| 47 | +> table already existed on `DateTimeFormatInfo` and was simply never called. |
| 48 | +> * **A limitation found by a test of mine FAILING** (#1945): `RoundtripKind` could not carry a kind |
| 49 | +> across a string, because nothing wrote a marker and nothing read one. It was declared and |
| 50 | +> pinned, and SA-16.3 later closed it — with the pin **inverted rather than deleted**. |
84 | 51 | > |
85 | | -> **#2410 IS `needs_user` AND IT IS THE ONE THING HELD OPEN DELIBERATELY.** Decision 3 is granted, |
86 | | -> but its **boundary** is not: measured, .NET in invariant globalization mode — this port's own mode |
87 | | -> — accepts **only `""` and `"und"`** and throws for **every** other name, *including `"de-DE"`* |
88 | | -> (`CultureData.cs:660-675`, `GlobalizationMode.cs:19`). The two readings (invariant-mode-exact vs a |
89 | | -> syntactic BCP-47 check) differ enormously in blast radius, so the choice was put to the user and |
90 | | -> **not made unilaterally**. **#1940 does not depend on it** and can proceed. |
| 52 | +> **Three recurring process lessons, each of which cost real time this stretch:** |
91 | 53 | > |
92 | | -> **#1940 IS CLOSED AND THE DATE/TIME CHAIN IS OPEN.** It was the root: **#1942, #1943 and #1945 |
93 | | -> are now `todo`**, and **#1944** follows through #1943. The blocker turned out to be **two** things |
94 | | -> where the ticket recorded one — the component cycle *and* the fact that **nothing in this runtime |
95 | | -> implemented `IFormatProvider` at all**, so there was nobody for `DateTime` to ask. Shape C's |
96 | | -> measured claim held exactly: two renames, **zero changed include lines**, graph still 41/94. |
| 54 | +> 1. **A mis-passed `-k` is worse than none, because it looks like success.** It appeared twice: |
| 55 | +> once as CMake printing a usage banner and exiting, once as ninja rejecting `-k` without a |
| 56 | +> number. Both reported *zero errors*. Check the **exit code**, never the error count alone. |
| 57 | +> 2. **A restore that reverts to the wrong baseline invalidates every verdict after it.** Using |
| 58 | +> `git checkout` on a file whose change was still **uncommitted** produced five consecutive |
| 59 | +> BUILD FAILED readings that were a harness state, not five findings. So did an anchor that |
| 60 | +> matched **twice**, and one that matched **nothing**. *An ambiguous or absent anchor is a |
| 61 | +> harness result, not a mutation result.* |
| 62 | +> 3. **A segfault is not a verdict.** Undefined behaviour must never be read as a mutation passing; |
| 63 | +> reformulate the mutation into a realistic defect and re-run. |
97 | 64 | > |
98 | | -> `ticket` has **3 `todo`** (#1942, #1943, #1945 — the newly unblocked chain); `task` has **0** |
99 | | -> unclassified (14,979 ignored / 1,082 ported / 140 ignore). Ticket totals: **2,394 done, 3 todo, 6 |
100 | | -> blocked, 2 needs_user, 5 wontfix**. Graph **41 / 94**, negative fixtures **52 / 264**. Seventeen |
101 | | -> tickets were filed or closed by this sweep: #2397–#2402 on 2026-08-19, #2403–#2409, #2411 and |
102 | | -> #1940 on 2026-08-20. |
| 65 | +> **And one operational fact about the downstream consumer, learned the hard way:** `cna`'s graphics |
| 66 | +> tests need a **virtual display**. Without one, `Texture2DCacheReconstructionTest` fails for |
| 67 | +> display reasons unrelated to any change under test. Run its suite under `xvfb-run`; it then reads |
| 68 | +> **6,332 tests, 0 failed**. A first attempt to attribute that failure compared a *filtered* run |
| 69 | +> against a *full* one, which is not a like-for-like measurement and proved nothing. |
103 | 70 | > |
104 | | -> **#1942 AND #1943 LISTED EACH OTHER, AND #2412 TOOK THE HALF THAT SEPARATES.** #1942 waited for |
105 | | -> an overload taking a `DateTimeStyles` (there was none, measured) and #1943 waited for #1942. |
106 | | -> `DateOnly`/`TimeOnly` **have no `DateTimeKind`**, so .NET rejects every kind-affecting style |
107 | | -> outright — **the styles that would need a timezone contract are exactly the styles that are |
108 | | -> illegal there** — and that half needed no approval. It is landed as **#2412**. |
| 71 | +> --- |
109 | 72 | > |
110 | | -> **Both tickets are back to `blocked`, on their `DateTime` halves alone**, which need **#1941 phase |
111 | | -> 2** and its unapproved timezone provider. That is a *user decision*, not a measurement: #1941's |
112 | | -> record says a phase-2 approval must name a date-sensitive timezone provider. |
| 73 | +> ## IF WORK IS WANTED AND THE QUEUES ARE STILL EMPTY |
113 | 74 | > |
114 | | -> **SA-15 WAS GRANTED ON 2026-08-20 AND IS THE STATE OF THE QUEUE.** Three decisions, all as |
115 | | -> recommended, in `docs/StandingApprovals.md`: (1) `DateTime` reaches a timezone through an |
116 | | -> **abstraction in `Core.Base`** rather than by moving `TimeZoneInfo` — **with the accepted caveat |
117 | | -> that .NET's `ToLocalTime()` takes no argument**, so the source must come from a hidden hook or an |
118 | | -> explicit overload, a deviation either way that must be *recorded* as one; (2) the culture-name |
119 | | -> boundary is a **syntactic BCP 47 check** — landed as **#2410**; (3) **SA-3 now covers vtable and |
120 | | -> base-class changes**, under five conditions whose fourth (enumerate every `catch` clause whose |
121 | | -> meaning changes) exists because a reparenting is invisible to a layout pin. |
122 | | -> |
123 | | -> **#1941 PHASE 2 IS LANDED AND THE DATE/TIME CHAIN IS FULLY OPEN.** `DateTime` converts by its |
124 | | -> `Kind` against an `ILocalTimeZone` the caller passes. **The recorded blocker looked at the wrong |
125 | | -> type**: `TimeZoneInfo` is date-INsensitive by its own documentation, while |
126 | | -> `System::TimeZone::CurrentTimeZone()` is per-date — and that is exactly the zone these |
127 | | -> conversions need, so the model was present all along. |
128 | | -> |
129 | | -> **#1980 IS CLOSED** — G-3 landed as the first change under SA-15.3, and G-1/G-2/G-4/G-5 landed |
130 | | -> 2026-08-19. **Four `todo`**: #1942, #1943, #1944 (unblocked by #1941 phase 2), #1945, plus #1997 |
131 | | -> (unblocked by SA-15.3, A-2/A-4 remaining). **Only three tickets remain blocked**, none of them on |
132 | | -> a decision this repository can take: #1773 and #2381 wait on downstream or merge events, #1962 on |
133 | | -> `CAP_NET_RAW`. |
134 | | -> |
135 | | -> **One thing #2406 deliberately did NOT do, so it is not mistaken for parity**: `DisplayAttribute`'s |
136 | | -> eight fields stay public data members (correct — .NET's are `{ get; set; }`), but their |
137 | | -> **nullability** still diverges (`string?`, `int?`, `bool?` with `GetOrder()`/ |
138 | | -> `GetAutoGenerateField()`), which is the #2295 shape across eight signatures. What remains *blocked* needs the user or an external event, |
139 | | -> and each is itemised in §2 below. **§4b says where to look next, and the method that found all |
140 | | -> three of today's tickets.** |
| 75 | +> The method that produced #2397, #2398, #2415 and #2416 is recorded because it keeps working: |
| 76 | +> **point the reference at what has no coverage of its own, and at assertions that cannot fail.** |
| 77 | +> Concretely — modules with no dedicated test executable; tests whose assertion is satisfied by the |
| 78 | +> defect they claim to pin (five were found this way); and the question *"what else in this runtime |
| 79 | +> asks this same question, and does it get the same answer?"*, which is what found #2401 from #2398 |
| 80 | +> and #2416 from #1945. |
141 | 81 |
|
142 | 82 | ## 2026-08-20 — #1944: multi-format `ParseExact` — **the last open post-audit ticket** |
143 | 83 |
|
|
0 commit comments