sourcecode 5.9.27__py3-none-any.whl → 5.9.28__py3-none-any.whl
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.
Potentially problematic release.
This version of sourcecode might be problematic. Click here for more details.
- sourcecode/__init__.py +1 -1
- sourcecode/_build_commit.py +1 -1
- sourcecode/_docs/DEFECT-LEDGER.md +119 -0
- sourcecode/_docs/USER_GUIDE.md +1 -1
- sourcecode/cache.py +81 -4
- sourcecode/cli.py +161 -6
- sourcecode/concurrency.py +264 -0
- sourcecode/context_cache.py +43 -2
- sourcecode/degradation.py +15 -0
- sourcecode/output_budget.py +49 -10
- sourcecode/parallel.py +35 -2
- sourcecode/repository_ir.py +149 -0
- sourcecode/resource_budget.py +106 -0
- sourcecode/spring_impact.py +17 -3
- {sourcecode-5.9.27.dist-info → sourcecode-5.9.28.dist-info}/METADATA +3 -3
- {sourcecode-5.9.27.dist-info → sourcecode-5.9.28.dist-info}/RECORD +20 -19
- {sourcecode-5.9.27.dist-info → sourcecode-5.9.28.dist-info}/WHEEL +0 -0
- {sourcecode-5.9.27.dist-info → sourcecode-5.9.28.dist-info}/entry_points.txt +0 -0
- {sourcecode-5.9.27.dist-info → sourcecode-5.9.28.dist-info}/licenses/LICENSE +0 -0
- {sourcecode-5.9.27.dist-info → sourcecode-5.9.28.dist-info}/licenses/NOTICE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/_build_commit.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""Generated at build time by hatch_build.py. Do not edit (AUD-592-D04)."""
|
|
2
2
|
|
|
3
|
-
BUILD_COMMIT = '
|
|
3
|
+
BUILD_COMMIT = 'adbbf9ca7652e2ad2dfdb2db911c12e1793dded5'
|
|
4
4
|
CLOSURE_PROVENANCE = {'AUD-611-B05': {'commit': '', 'status': 'uncited'}, 'AUD-611-B06': {'commit': '', 'status': 'uncited'}, 'AUD-611-B07': {'commit': '', 'status': 'uncited'}, 'AUD-611-B08': {'commit': '', 'status': 'uncited'}, 'AUD-611-B09': {'commit': '', 'status': 'uncited'}, 'AUD-611-B10': {'commit': '', 'status': 'uncited'}, 'AUD-611-B11': {'commit': '', 'status': 'uncited'}, 'AUD-612-07': {'commit': '', 'status': 'uncited'}, 'AUD-612-09': {'commit': '', 'status': 'uncited'}, 'AUD-612-14': {'commit': '', 'status': 'uncited'}, 'AUD-612-B03': {'commit': '', 'status': 'uncited'}, 'AUD-612-B07': {'commit': '', 'status': 'uncited'}, 'AUD-612-B13': {'commit': '', 'status': 'uncited'}, 'AUD-612-E': {'commit': '', 'status': 'uncited'}, 'AUD-612-G': {'commit': '', 'status': 'uncited'}, 'AUD-612-I': {'commit': '', 'status': 'uncited'}, 'AUD-612-K': {'commit': '', 'status': 'uncited'}, 'AUD-612-M': {'commit': '', 'status': 'uncited'}, 'AUD-612-R': {'commit': '', 'status': 'uncited'}, 'AUD-613-ROOT': {'commit': '', 'status': 'uncited'}, 'AUD-614-07': {'commit': '', 'status': 'uncited'}, 'AUD-614-E': {'commit': '', 'status': 'uncited'}, 'AUD-614-F': {'commit': '', 'status': 'uncited'}, 'AUD-614-G': {'commit': '', 'status': 'uncited'}, 'AUD-614-I': {'commit': '', 'status': 'uncited'}, 'AUD-614-INTENT-TIE': {'commit': '', 'status': 'uncited'}, 'AUD-614-JUNIT4': {'commit': '', 'status': 'uncited'}, 'AUD-614-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-614-SELFTEST-TIMEOUT': {'commit': '', 'status': 'uncited'}, 'AUD-615-07': {'commit': '', 'status': 'uncited'}, 'AUD-615-COLD-COMPACT': {'commit': '', 'status': 'uncited'}, 'AUD-615-DRIFT': {'commit': '', 'status': 'uncited'}, 'AUD-615-E': {'commit': '', 'status': 'uncited'}, 'AUD-615-F': {'commit': '', 'status': 'uncited'}, 'AUD-615-INTENT-TIE': {'commit': '', 'status': 'uncited'}, 'AUD-615-JUNIT4': {'commit': '', 'status': 'uncited'}, 'AUD-615-RISK-L0': {'commit': '', 'status': 'uncited'}, 'AUD-615-SELFTEST-TIMEOUT': {'commit': '', 'status': 'uncited'}, 'AUD-616-E-PROV': {'commit': '', 'status': 'uncited'}, 'AUD-616-F, -RISK-L0, -SELFTEST-TIMEOUT, -INTENT-TIE, -DRIFT, -COLD-COMPACT, -E, -07': {'commit': '', 'status': 'uncited'}, 'AUD-616-INTENT-TIE-COV': {'commit': '', 'status': 'uncited'}, 'AUD-616-JUNIT4-SELF': {'commit': '', 'status': 'uncited'}, 'AUD-616-KOTLIN': {'commit': '', 'status': 'uncited'}, 'AUD-616-O': {'commit': '', 'status': 'uncited'}, 'AUD-616-P': {'commit': '', 'status': 'uncited'}, 'Window': {'commit': '', 'status': 'uncited'}, '`ASK-FIELD-01`': {'commit': '829f9fa', 'status': 'present'}, '`ASK-FIELD-02`': {'commit': '6a659fa', 'status': 'present'}, '`ASK-FIELD-03`': {'commit': '55b0d51', 'status': 'present'}, '`AUD-588-F05`': {'commit': '470e4a4', 'status': 'present'}, '`AUD-590-B01` / `B-01`, `F-01`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B02` / `B-02`, `F-02`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B03` / `B-03`, `F-03`': {'commit': '', 'status': 'uncited'}, '`AUD-590-B04` / `B-04`, `F-04`': {'commit': '', 'status': 'uncited'}, '`AUD-590-R01` / `R-01`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A01` / `A-1`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A02` / `A-2`, `A-4`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A03` / `A-3`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A05` / `A-5`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A06` / `A-6`, `A-7`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A08` / `A-8`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A09` / `A-9`': {'commit': '', 'status': 'uncited'}, '`AUD-591-A10` / `A-10`': {'commit': '', 'status': 'uncited'}, '`AUD-592-A02` / `A-1` second half, `§12.5`': {'commit': '63f3d61', 'status': 'present'}, '`AUD-592-B01` / `B-8`': {'commit': '8329614', 'status': 'present'}, '`AUD-592-D01` / `D-1`': {'commit': '0f8e03f', 'status': 'present'}, '`AUD-592-D02` / `D-2`': {'commit': '1d3db4d', 'status': 'present'}, '`AUD-592-D03` / `D-3`': {'commit': 'f28fe40', 'status': 'present'}, '`AUD-592-R01` / `A-1`': {'commit': '216025f', 'status': 'present'}, '`AUD-592-R02` / `N-4`, reopens `AUD-513-N04`': {'commit': 'ec3050d', 'status': 'present'}, '`AUD-593-N01` / `N-01`': {'commit': 'dd6a706', 'status': 'present'}, '`AUD-593-N02` / `N-02`': {'commit': 'f539787', 'status': 'present'}, '`AUD-593-N03` / `N-03`': {'commit': '1dd0b1c', 'status': 'present'}, '`AUD-593-N05` / `N-05`': {'commit': 'aa89388', 'status': 'present'}, '`AUD-593-N06` / `N-06`': {'commit': 'aa584a6', 'status': 'present'}, '`AUD-594-N04` / `D-5`, disclosure half of `B-3`': {'commit': 'a0060b7', 'status': 'present'}, '`AUD-594-X01` / `A-1`, residual of `AUD-593-N03`': {'commit': 'dd222b9', 'status': 'present'}, '`AUD-594-X02` / `A-1` second half': {'commit': '1193937', 'status': 'present'}, '`AUD-594-X03` / `X-03`, residual of `AUD-593-N01`': {'commit': 'e89e8f0', 'status': 'present'}, '`AUD-594-X04`': {'commit': '05b8cc9', 'status': 'present'}, '`AUD-595-A02` / `N-5`, advisory half of `AUD-588-B11`': {'commit': '3e15227', 'status': 'present'}, '`AUD-595-A03` / `B-6` exit-code half': {'commit': 'ab7285f', 'status': 'present'}, '`AUD-595-A05`': {'commit': 'e0b3d10', 'status': 'present'}, '`AUD-595-A07`, narrow half of `AUD-592-A02`': {'commit': 'cc15177', 'status': 'present'}, '`AUD-595-B01` / §B': {'commit': 'b77a1ad', 'status': 'present'}, '`AUD-595-Q01`': {'commit': '91a6566', 'status': 'present'}, '`AUD-596-A01` / `ASK-DET-001`': {'commit': 'fa508b4', 'status': 'present'}, '`AUD-596-A02` / `ASK-SELF-001`': {'commit': '4dfadc8', 'status': 'present'}, '`AUD-596-A03` / `ASK-C1-001`': {'commit': 'bffa3a6', 'status': 'present'}, '`AUD-596-A04` / `ASK-UX-003` + `ASK-UX-004`': {'commit': 'f0aac3c', 'status': 'present'}, '`AUD-596-B01` / `B-01`, harder witness for `BUG-6`': {'commit': '567d09d', 'status': 'present'}, '`AUD-596-B02` / `B-02`, class residual of `R2`': {'commit': '', 'status': 'uncited'}, '`AUD-596-B03` / `B-03`': {'commit': 'a8cb814', 'status': 'present'}, '`AUD-596-B06` / `B-06`': {'commit': '8107927', 'status': 'present'}, '`AUD-596-B07` / `B-07`, class residual of `AUD-592-R02`': {'commit': 'e060485', 'status': 'present'}, '`AUD-596-B09` / `B-09`': {'commit': '5b8146c', 'status': 'present'}, '`AUD-596-B10` / `B-10`': {'commit': '5cb994d', 'status': 'present'}, '`AUD-596-B16` / `B-16`': {'commit': '1507f69', 'status': 'present'}, '`AUD-596-D01` / `B-13` + `B-14`': {'commit': '0b1bdf0', 'status': 'present'}, '`AUD-596-D02` / `B-08`, residual of `AUD-594-N04`': {'commit': 'e7f73c3', 'status': 'present'}, '`AUD-596-D03` / `ASK-AGT-001`': {'commit': '8c46967', 'status': 'present'}, '`AUD-596-D04` / `B-18`': {'commit': '040f027', 'status': 'present'}, '`AUD-596-D05` / `B-19`': {'commit': 'fd2bf68', 'status': 'present'}, '`AUD-596-D06` / `B-20`': {'commit': '784cd83', 'status': 'present'}, '`AUD-596-D07` / `B-17`, half refuted': {'commit': 'c3ff822', 'status': 'present'}, '`AUD-596-D08` / `B-22`, class residual of `AUD-591-A06`': {'commit': 'b396c6a', 'status': 'present'}, '`AUD-596-D09` / `B-23`, surviving half of `B26`': {'commit': 'fd3d63f', 'status': 'present'}, '`AUD-596-D10` / `B-29`': {'commit': '', 'status': 'uncited'}, '`AUD-596-D11` / `ASK-UX-005`': {'commit': 'f4b4f77', 'status': 'present'}, '`AUD-596-D12` — six hints that send the reader back to the error': {'commit': '498df44', 'status': 'present'}, '`AUD-596-D13` / `B-28`': {'commit': '1014c72', 'status': 'present'}, '`AUD-596-D14` / `B-15`, narrow half of `B20`': {'commit': '10f2827', 'status': 'present'}, '`AUD-596-R01` / `B-05`': {'commit': '', 'status': 'uncited'}, '`AUD-596-X01` / `ASK-GATE-001`, gating half of `B24`': {'commit': 'e59c655', 'status': 'present'}, '`AUD-596-X02` / `ASK-UX-001` + `ASK-UX-002` + `B-21`': {'commit': 'f6ebf50', 'status': 'present'}, '`AUD-597-A01` / `ASK-UX-001` + `N-01`': {'commit': 'f8cf061', 'status': 'present'}, '`AUD-597-A02` / `ASK-UX-008` + `ASK-DOC-002`': {'commit': '9d47a4c', 'status': 'present'}, '`AUD-597-A03` / `ASK-DET-001`': {'commit': '4c7622a', 'status': 'present'}, '`AUD-597-A04` / `ASK-LEDGER-001`': {'commit': '46fb870', 'status': 'present'}, '`AUD-597-D01` / `B-04`': {'commit': '1094e6a', 'status': 'present'}, '`AUD-597-D02` / `ASK-DOC-001`': {'commit': 'ecb1bb7', 'status': 'present'}, '`AUD-597-D03` / `N-06`': {'commit': '9784333', 'status': 'present'}, '`AUD-597-D04` / `N-05`': {'commit': 'e51c087', 'status': 'present'}, '`AUD-597-D05` / `N-04`, fourth appearance of `C4-27`': {'commit': '9cccc15', 'status': 'present'}, '`AUD-597-D06` / `ASK-CLI-001`': {'commit': '178aa6d', 'status': 'present'}, '`AUD-597-D07` / `N-03`': {'commit': '9245e35', 'status': 'present'}, '`AUD-597-R01` / `ASK-PERF-001`': {'commit': '6910054', 'status': 'present'}, '`AUD-597-R02` / `ASK-ENC-001`': {'commit': 'ce6dac1', 'status': 'present'}, '`AUD-597-R03` / `ASK-UX-002`': {'commit': 'c57e50b', 'status': 'present'}, '`AUD-597-X01` / `ASK-BUILD-001` + `ASK-META-001` / `B-12`': {'commit': '354338b', 'status': 'present'}, '`AUD-598-B01` / `ASK-META-001`': {'commit': 'b2f7c65', 'status': 'present'}, '`AUD-598-B02` / `ASK-UX-002`': {'commit': '56a4bf7', 'status': 'present'}, '`AUD-598-B03` / `B-11`': {'commit': 'c63cb24', 'status': 'present'}, '`AUD-598-B04` / `N-02`': {'commit': '187937f', 'status': 'present'}, '`AUD-598-R01` / `ASK-PERF-002`': {'commit': '', 'status': 'uncited'}, '`AUD-598-X01` / `N-01` + `B-13` + `B-14` + `B-29` + `N-06`': {'commit': '6f2f5a4', 'status': 'present'}, '`AUD-600-B01` / `P-01`': {'commit': '366ffed', 'status': 'present'}, '`AUD-600-B02` / `P-02` + `ASK-PACK-002`': {'commit': '2a814c3', 'status': 'present'}, '`AUD-600-B03` / `ASK-PACK-001`': {'commit': '452f2a0', 'status': 'present'}, '`AUD-600-D01`': {'commit': '1224e1b', 'status': 'present'}, '`AUD-600-D02`': {'commit': 'eab8172', 'status': 'present'}, '`AUD-600-R01` / `R-01`': {'commit': '88add64', 'status': 'present'}, '`AUD-601-B01` / `N-02`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-B02` / `ASK-PACK-004`': {'commit': '', 'status': 'uncited'}, '`AUD-601-D01` / `N-01`, `ASK-UX-001`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-D02` / `B-13`, `B-14`, 5th round': {'commit': '', 'status': 'uncited'}, '`AUD-601-M01` / `B-26`, `B-27` retracted': {'commit': '', 'status': 'uncited'}, '`AUD-601-R01` / `R-02`': {'commit': '', 'status': 'uncited'}, '`AUD-619-WATCH`': {'commit': '', 'status': 'uncited'}, '`AUD-620-COMMAND-SURFACE`': {'commit': '', 'status': 'uncited'}, '`AUD-620-REVIEW-PR`': {'commit': '', 'status': 'uncited'}, '`AUD-621-REVIEW-PR`': {'commit': '', 'status': 'uncited'}, '`B-3`': {'commit': '773268d', 'status': 'present'}, '`B-6`': {'commit': '17c6e06', 'status': 'present'}, '`B-8`': {'commit': '8329614', 'status': 'present'}, '`BUG-15`': {'commit': '', 'status': 'uncited'}, '`BUG-21`': {'commit': '', 'status': 'uncited'}, '`BUG-3` / `443b345`': {'commit': '', 'status': 'uncited'}, '`BUG-4a` / `AUD-588-B11` residual': {'commit': '24b4185', 'status': 'present'}, '`BUG-4b` / `AUD-588-B11` residual': {'commit': '0376350', 'status': 'present'}, '`E-39`, class residual of `AUD-593-N06`': {'commit': '25fc11f', 'status': 'present'}, '`N-5`': {'commit': 'c68083f', 'status': 'present'}, '`N-7`': {'commit': '082e9de', 'status': 'present'}, '`R-1`': {'commit': '424fb1c', 'status': 'present'}, '`R-2`': {'commit': '5158b92', 'status': 'present'}, '`R-3`': {'commit': '88aec04', 'status': 'present'}, '`R-4`': {'commit': '04fd04f', 'status': 'present'}, '`REG-01`': {'commit': '', 'status': 'uncited'}, '`REG-04`': {'commit': '', 'status': 'uncited'}}
|
|
5
5
|
CLOSURE_COVERAGE = {'closed_rows': 169, 'present': 91, 'absent': 0, 'unresolved': 0, 'uncited': 78, 'unknown': 0}
|
|
@@ -14,8 +14,127 @@ Status values: `open` · `in-progress` · `closed <version>` · `by-design` (wit
|
|
|
14
14
|
The facts these rows are keyed to are published: `ask schema facts-v1` prints the registry
|
|
15
15
|
(authority, definition, consumers, parity test) that the release battery enforces.
|
|
16
16
|
|
|
17
|
+
## Current Synchronization — Release `5.9.28`, nine rows closed on the 5.9.27 queue (2026-09-02)
|
|
18
|
+
|
|
19
|
+
**`R-01`'s architectural question is answered in source, and the answer was
|
|
20
|
+
"exclusive use".** `parallel.default_jobs()` returned `cpu_count() - 1` for
|
|
21
|
+
each process independently, so `k` concurrent `ask` invocations started
|
|
22
|
+
`k × (N-1)` parsing workers on `N` cores — and no field in any payload said the
|
|
23
|
+
run had been sharing the machine. That is what four rounds of "same command,
|
|
24
|
+
sometimes fast, sometimes hung, no per-process variable distinguishing the two"
|
|
25
|
+
looks like from outside. The cache was *not* the same kind of defect: writes
|
|
26
|
+
are unique-temp + `os.replace` with retry and every read is guarded, so a
|
|
27
|
+
concurrent writer degrades a peer to a cache miss; the one operation that hurt
|
|
28
|
+
a peer was eviction, which now defers while a peer is live.
|
|
29
|
+
|
|
30
|
+
Every row marked **fixed** below is closed in release `5.9.28`; the two
|
|
31
|
+
open rows say what closure still needs. This batch does not claim to close
|
|
32
|
+
the erratic-failure row — one hang was
|
|
33
|
+
observed with nothing else running, and that is unexplained. It removes the
|
|
34
|
+
mechanism the 5.9.27 round evidenced, and gives the next round the variable it
|
|
35
|
+
did not have: `_meta.concurrency` on every answer.
|
|
36
|
+
|
|
37
|
+
**Measured here, for the two latency rows:** `ask --help` is 0.41–0.44 s on
|
|
38
|
+
this machine over three consecutive runs (the audit measured 14.4–14.7 s under
|
|
39
|
+
confirmed contention). That is not a refutation of the field figure — it is a
|
|
40
|
+
different machine — but it does put the row behind `R-01` rather than beside
|
|
41
|
+
it, and the next round can separate them with the disclosure block above.
|
|
42
|
+
|
|
43
|
+
| ID | Severity | Status | Evidence and required closure |
|
|
44
|
+
|---|---:|---|---|
|
|
45
|
+
| `R-01` (contention) | Critical maximum | **mechanism removed, row open pending field re-measure** | `concurrency.py` keeps a best-effort census of live invocations (one lease per process, swept by pid liveness on POSIX and lease age everywhere — never `os.kill(pid, 0)` on Windows, where CPython maps a non-console signal onto `TerminateProcess` and the POSIX idiom would kill the peer it was counting). `parallel.default_jobs()` divides the worker budget by that census; `cache._gc` defers eviction while a peer is live; `_meta.concurrency` publishes peers at start, whether the budget was shared, and that a shared run is not an isolated-machine measurement. `ASK_NO_CONCURRENCY_CENSUS=1` restores the previous behaviour. Closure needs a field round measuring the same commands with the block attached. |
|
|
46
|
+
| `BUG-22` / `REG-02` (enforcement half) | High | **fixed** | The ceiling was checked at phase boundaries only, so a phase could allocate without bound and be refused once it finished — the 2 029.92 MB peak under a 1 536 MB ceiling that was genuinely in force. `resource_budget` now arms a ceiling, samples it inside the parse loop (throttled to one footprint sample per 0.5 s; a per-file call costs one clock read) and raises a typed `CeilingExceeded` that the CLI renders as the same `MEMORY_TOO_LARGE` refusal the boundary check emits. Deliberately not a sampler thread: a thread can see the crossing but cannot unwind the main thread anywhere safe. `_meta.memory.enforcement` publishes when the ceiling is checked. |
|
|
47
|
+
| `BUG-10` | High | **fixed** | The zero-endpoint answer had four named detections and everything else fell through to "no recognized routing construct was found", which reads as *no HTTP surface*. `unmodeled_routing_evidence` now publishes files declaring an annotation whose argument is a path-like string literal that produced no endpoint — a rule that names no framework, per this repository's vendor-agnostic convention. Measured: `struts` reports 4 files under `@Action`/`@Namespace` where the answer used to say nothing was found; `spring-petclinic` reports none. |
|
|
48
|
+
| `NEW-02` / `BUG-05` | High | **fixed** | Overloaded handlers all inherited the final overload's line because the symbol index is keyed `Class#method` with no parameter types. The extractor has no signature to disambiguate with, so the row's other branch applies: `overload_count`, `anchor_ambiguity` and `anchor_candidate_lines` are published per entry, with `anchor_ambiguity_note` once beside the list, and an unambiguous anchor carries none of it. |
|
|
49
|
+
| `BUG-13` | High | **fixed** | `selftest .` over nested repositories warned and then ran the ledger against the workspace — the shape behind `selftest` falling from 9/12 to 1/12 rows inside one 120 s deadline. The implicit `.` now refuses with `INVALID_USAGE` before any row runs, naming the count, with `--allow-nested` for a caller who means the workspace. |
|
|
50
|
+
| `BUG-16` | High | **fixed** | The context cache's caps (30-day TTL, 256 entries) were real and lived inside `_gc`, so ~1.7 GB of growth had no readable contract. `cache.eviction_contract()` and `context_cache.eviction_contract()` are now the one authority each `_gc` reads, and `cache status` publishes them per store in JSON and text. The parse store already published its budget; its block projects those same figures rather than measuring them again. |
|
|
51
|
+
| `ALT-08` (arithmetic half) | Low | **fixed** | `_truncation_summary.final_size_bytes` was measured before the summary and `_budget_note` were attached, so the one part of the payload the accounting omitted was the accounting. It is now measured over the assembled object and republished to a fixed point, with `final_size_basis` written before the fixed point is sought and `content_size_bytes` keeping the trimmed-content figure under a name that says which it is. |
|
|
52
|
+
| `ALT-25` (`impact-chain` audit clock) | Low | **fixed** | `metadata.audit_time_ms` was always ~0.0 on the default path and the 5.9.25 round read it as a security phase skipped by `ALT-03`. It is not: `include_findings` is opt-in, so the clock measured the elapsed time of *not* running an audit. It is `null` when `findings_status` is `not_computed`, and `query_time_basis` states the rule. |
|
|
53
|
+
| `retrieve security-surface --target` | Low | **fixed** | Four rounds tried it and got "No such option: --target". The intent is repository-wide by construction, and the refusal now says so and names `security-chain --target`, derived from `intent_router.ANCHOR_KINDS` rather than a second list. |
|
|
54
|
+
| `ask --help` latency | Low | **open, not reproduced here** | 0.41–0.44 s over three runs on the engineering host against 14.4–14.7 s in the field under confirmed contention. Re-measure with `_meta.concurrency` attached before treating it as its own row. |
|
|
55
|
+
| `ALT-07` (`remediate` hang), `ALT-10` (`onboard`) | Medium | **open, unchanged** | Both share the erratic signature `R-01` now instruments. No separate mechanism found in source this round; neither reproduces on the engineering host. |
|
|
56
|
+
| `BUG-02`, `BUG-12` | Critical maximum | **open, unchanged** | Both need the field's own large repository to reproduce (`rc=127` with no output; multi-gigabyte RSS while building a small answer). `BUG-22`'s sampler bounds the second one's damage without closing it. |
|
|
57
|
+
|
|
58
|
+
## Current Synchronization — Alternative External Audit `5.9.27`, shared-machine contention confirmed (2026-09-02)
|
|
59
|
+
|
|
60
|
+
**First score uptick in four rounds (3.8 → 4.6/10), with a large
|
|
61
|
+
qualification found mid-session: the audit machine was shared with a second,
|
|
62
|
+
independent `ask` test harness.** The native-Windows auditor found
|
|
63
|
+
`entry-trace.jsonl` (this ledger's own diagnostic, added last round) growing
|
|
64
|
+
with invocations they never launched, against five OSS reference
|
|
65
|
+
repositories under the exact same `timeout N ask ...` harness pattern they
|
|
66
|
+
use, and confirmed a live foreign `ask.exe` process via `Get-CimInstance
|
|
67
|
+
Win32_Process` under their own Windows user. Two of their own `spring-audit`
|
|
68
|
+
hangs overlapped a 525-second window of 34 foreign invocations. Full
|
|
69
|
+
evidence:
|
|
70
|
+
[`AUDIT-2026-09-02-ALTERNATIVE-5.9.27.md`](AUDIT-2026-09-02-ALTERNATIVE-5.9.27.md).
|
|
71
|
+
|
|
72
|
+
**`R-01` (new, highest priority):** if ASK's shared worker pool and cache do
|
|
73
|
+
not isolate concurrent `ask` processes on one machine, contention could
|
|
74
|
+
produce exactly the pattern reported since 5.9.23 — the same command
|
|
75
|
+
sometimes hanging, sometimes returning empty near-instantly, no per-process
|
|
76
|
+
variable distinguishing the two. This is a real, evidenced contributing
|
|
77
|
+
factor, not a proven sole cause (one hang occurred after 20 seconds of no
|
|
78
|
+
foreign activity at all). The open question for engineering: is ASK's worker
|
|
79
|
+
pool designed to tolerate concurrent `ask` invocations on one machine, or
|
|
80
|
+
does it assume exclusive use? If the latter, that is an architectural
|
|
81
|
+
constraint, not a series of unrelated command bugs, and changes where the
|
|
82
|
+
fix belongs.
|
|
83
|
+
|
|
84
|
+
**Entry-point trace progress, confirmed:** the auditor's own invocations —
|
|
85
|
+
including ones that returned empty or hung — now appear in `entry-trace.jsonl`
|
|
86
|
+
(the fix from last round's `R-01`). This confirms `main_entry` is reached on
|
|
87
|
+
the failing runs. `empty_answer_diagnostics.record()` still never fired on
|
|
88
|
+
any observed failure this session, which narrows the search specifically to
|
|
89
|
+
somewhere between `main_entry` and the output-promise guard — a materially
|
|
90
|
+
smaller surface than "somewhere in the process," three rounds ago.
|
|
91
|
+
|
|
92
|
+
| ID | Severity | Status | Evidence and required closure |
|
|
93
|
+
|---|---:|---|---|
|
|
94
|
+
| `R-01` (contention) | Critical maximum | **open, new evidenced hypothesis** | Confirmed shared-machine contention during this audit session (foreign `ask` process, matching harness pattern, overlapping timing with 2 of the auditor's own hangs). Needs an internal answer, not more external symptom-chasing: does the worker pool serialize or isolate concurrent invocations, or assume exclusive use? If it assumes exclusive use, `ALT-01`/`ALT-03`/`NEW-01` may be one architectural defect, not several. |
|
|
95
|
+
| `R-01` (entry trace) | — | **confirmed working, narrows the search** | The auditor's own failing invocations now appear in `entry-trace.jsonl`. `main_entry` is reached; the gap is between there and `_fail_on_missing_required_answer`/`_fail_on_unkept_output_promise`, which still never fire. |
|
|
96
|
+
| `ALT-09` | Critical | **fixed, reconfirmed a third session** | Piped `n` to `cache clear` aborts correctly. |
|
|
97
|
+
| `BUG-15` / `BUG-21` | Low | **fixed, reconfirmed** | No markup leak; `--jobs` range validated. |
|
|
98
|
+
| `R-05` from last round (`selftest` itemization) | High | **fixed, reconfirmed** | `"10 ledger row(s) could not run: ASK-16, B6, E-37, ..."` replaces the prior generic slug. |
|
|
99
|
+
| `onboard` | Medium | **open, still the most erratic single measurement** | 904ms to a full 200s external kill, same invocation, same tree, within one session. Cross-reference against `entry-trace.jsonl` timing before assuming a cold/warm explanation. |
|
|
100
|
+
| `ask --help` latency | Low | **open, persisted a second round** | 14.4-14.7s this session (was 3-4s last round, flagged then as "worth its own row if it persists"). It has persisted. Not verified in isolation from `R-01`'s contention this session. |
|
|
101
|
+
|
|
102
|
+
The remaining rows (`BUG-01`, `BUG-03`, `BUG-06`, `BUG-08` through `BUG-23`,
|
|
103
|
+
`REG-02`, `REG-03`, `NEW-02`) were not relaunched this round for time budget;
|
|
104
|
+
no regression signal against them. `watch`'s ~55s warm-before-first-tick and
|
|
105
|
+
`retrieve security-surface --target` remain unchanged, correctly understood
|
|
106
|
+
as real cost and a known gap respectively, not new defects.
|
|
107
|
+
|
|
108
|
+
**Corroborating data point, unverified in detail:** a separate exchange the
|
|
109
|
+
same session referenced a distinct "External" lineage audit of 5.9.27 scoring
|
|
110
|
+
**8.1/10** on the five OSS reference repositories found in the trace above —
|
|
111
|
+
consistent with the shared-machine hypothesis (two lineages, two repo
|
|
112
|
+
populations, one machine) but not independently confirmed here; the
|
|
113
|
+
transcript fragment available was too corrupted to verify its table in
|
|
114
|
+
detail and is not published as a standalone audit document.
|
|
115
|
+
|
|
17
116
|
## Current Synchronization — External Audit `5.9.26`, self-contradictory session (2026-09-02)
|
|
18
117
|
|
|
118
|
+
> **Provenance correction, added during the 5.9.27 round.** The
|
|
119
|
+
> native-Windows auditor for 5.9.27 states plainly that the "initial
|
|
120
|
+
> 129-invocation pass ... 0/23 ... 7.4/10" described below does not
|
|
121
|
+
> correspond to anything they ran. The 5.9.27 round independently confirmed
|
|
122
|
+
> the audit machine was shared with a second, concurrent `ask` test harness
|
|
123
|
+
> exercising five OSS reference repositories (`thingsboard`, `mall`,
|
|
124
|
+
> `struts`, `spring-petclinic`, `jhipster-sample-app`) — a repo set and
|
|
125
|
+
> pattern that matches the "External" multi-repository lineage this ledger
|
|
126
|
+
> has tracked separately since 5.9.22 (`AUDIT-2026-09-02-EXTERNAL-5.9.26.md`
|
|
127
|
+
> below, and the 6.3→3.8 scores it reflects). The likely explanation is that
|
|
128
|
+
> the two-pass narrative below mixed observations from two independent,
|
|
129
|
+
> concurrent sessions on one shared machine rather than being one
|
|
130
|
+
> evaluator's self-revision. Left as written below rather than rewritten,
|
|
131
|
+
> with this correction attached, per this ledger's own discipline of
|
|
132
|
+
> recording when it got something wrong rather than quietly fixing the
|
|
133
|
+
> record. See
|
|
134
|
+
> [`AUDIT-2026-09-02-ALTERNATIVE-5.9.27.md`](AUDIT-2026-09-02-ALTERNATIVE-5.9.27.md)
|
|
135
|
+
> for the evidence (`entry-trace.jsonl` cross-contamination, a live foreign
|
|
136
|
+
> process confirmed via `Get-CimInstance Win32_Process`).
|
|
137
|
+
|
|
19
138
|
**The 5.9.26 fix batch did not close the release gate, and new evidence
|
|
20
139
|
narrows the failure to before the guard rather than inside it.** This audit
|
|
21
140
|
round is internally contradictory and both halves are published rather than
|
sourcecode/_docs/USER_GUIDE.md
CHANGED
sourcecode/cache.py
CHANGED
|
@@ -151,6 +151,49 @@ _DEFAULT_KEEP_COMMITS: int = 5
|
|
|
151
151
|
_DEFAULT_MAX_CORES: int = 20
|
|
152
152
|
_DEFAULT_MAX_SIZE_MB: int = 50
|
|
153
153
|
|
|
154
|
+
|
|
155
|
+
def eviction_contract() -> "dict[str, Any]":
|
|
156
|
+
"""The three caps this store evicts under, read once (BUG-16).
|
|
157
|
+
|
|
158
|
+
`_gc` used to read the environment inline, so the only way to learn what
|
|
159
|
+
bounded the cache was to read the sweep. A consumer asking `cache status`
|
|
160
|
+
what will be evicted, and when, now gets the same numbers the sweep uses.
|
|
161
|
+
"""
|
|
162
|
+
def _int(name: str, default: int) -> int:
|
|
163
|
+
try:
|
|
164
|
+
return int(os.environ.get(name, default))
|
|
165
|
+
except (TypeError, ValueError):
|
|
166
|
+
return default
|
|
167
|
+
|
|
168
|
+
keep = _int("SOURCECODE_CACHE_KEEP_COMMITS", _DEFAULT_KEEP_COMMITS)
|
|
169
|
+
max_cores = _int("SOURCECODE_CACHE_MAX_CORES", _DEFAULT_MAX_CORES)
|
|
170
|
+
max_size_mb = _int("SOURCECODE_CACHE_MAX_SIZE_MB", _DEFAULT_MAX_SIZE_MB)
|
|
171
|
+
clauses = []
|
|
172
|
+
if keep > 0:
|
|
173
|
+
clauses.append(f"the last {keep} distinct commits are kept")
|
|
174
|
+
if max_cores > 0:
|
|
175
|
+
clauses.append(f"at most {max_cores} cores, least recently used evicted first")
|
|
176
|
+
if max_size_mb > 0:
|
|
177
|
+
clauses.append(f"the store is trimmed toward {max_size_mb} MB")
|
|
178
|
+
return {
|
|
179
|
+
"keep_commits": keep,
|
|
180
|
+
"max_cores": max_cores,
|
|
181
|
+
"max_size_mb": max_size_mb,
|
|
182
|
+
"keep_commits_env": "SOURCECODE_CACHE_KEEP_COMMITS",
|
|
183
|
+
"max_cores_env": "SOURCECODE_CACHE_MAX_CORES",
|
|
184
|
+
"max_size_env": "SOURCECODE_CACHE_MAX_SIZE_MB",
|
|
185
|
+
"policy": (
|
|
186
|
+
"; ".join(clauses) + ", swept after each write"
|
|
187
|
+
if clauses else
|
|
188
|
+
"no eviction — every cap was set to 0 deliberately"
|
|
189
|
+
),
|
|
190
|
+
"deferred_while_shared": (
|
|
191
|
+
"A sweep is skipped while another `ask` invocation is live on this "
|
|
192
|
+
"machine (R-01): eviction would delete the working set a concurrent "
|
|
193
|
+
"run is reading."
|
|
194
|
+
),
|
|
195
|
+
}
|
|
196
|
+
|
|
154
197
|
#: Windows hardening for _atomic_write: os.replace can raise PermissionError
|
|
155
198
|
#: (WinError 5/32) when an antivirus scanner, search indexer, or concurrent
|
|
156
199
|
#: reader transiently holds the destination open. Retry briefly to ride it out.
|
|
@@ -501,27 +544,45 @@ def _store_breakdown(repo_root: Path, core_bytes: int) -> "dict[str, Any]":
|
|
|
501
544
|
"bytes": core_bytes,
|
|
502
545
|
"scope": "this repository",
|
|
503
546
|
"holds": "core snapshots, rendered views and their CAS blobs",
|
|
547
|
+
"eviction": eviction_contract(),
|
|
504
548
|
},
|
|
505
549
|
}
|
|
506
550
|
try:
|
|
507
551
|
from sourcecode.context_cache import ContextCache # noqa: PLC0415
|
|
508
552
|
|
|
509
553
|
ctx = ContextCache.for_repo(repo_root).stats()
|
|
554
|
+
from sourcecode.context_cache import ( # noqa: PLC0415
|
|
555
|
+
eviction_contract as _ctx_eviction,
|
|
556
|
+
)
|
|
557
|
+
|
|
510
558
|
out["shared_cir"] = {
|
|
511
559
|
"cache_dir": ctx["cache_dir"],
|
|
512
560
|
"entries": ctx["contexts"],
|
|
513
561
|
"bytes": ctx["bytes_stored"],
|
|
514
562
|
"scope": "this repository",
|
|
515
563
|
"holds": "the shared Canonical IR that explain/impact/posture reuse",
|
|
564
|
+
# BUG-16: measured at ~1.7 GB in two rounds with nothing published
|
|
565
|
+
# about what bounds it. The caps were real and lived inside `_gc`.
|
|
566
|
+
"eviction": _ctx_eviction(),
|
|
516
567
|
}
|
|
517
568
|
except Exception:
|
|
518
569
|
out["shared_cir"] = {"available": False}
|
|
519
570
|
try:
|
|
520
571
|
from sourcecode import parse_cache as _pc # noqa: PLC0415
|
|
521
572
|
|
|
573
|
+
_parse_stats = _pc.store_stats()
|
|
522
574
|
out["parse"] = {
|
|
523
|
-
**
|
|
575
|
+
**_parse_stats,
|
|
524
576
|
"holds": "per-file parses, content-addressed across every repository",
|
|
577
|
+
# BUG-16: this store already published its budget under its own
|
|
578
|
+
# names; the block below is that same answer projected under the
|
|
579
|
+
# name every store now answers to, never a second measurement.
|
|
580
|
+
"eviction": {
|
|
581
|
+
"policy": _parse_stats.get("policy"),
|
|
582
|
+
"budget_mb": _parse_stats.get("budget_mb"),
|
|
583
|
+
"budget_env": _parse_stats.get("budget_env"),
|
|
584
|
+
"sweep_interval_mb": _parse_stats.get("sweep_interval_mb"),
|
|
585
|
+
},
|
|
525
586
|
}
|
|
526
587
|
except Exception:
|
|
527
588
|
out["parse"] = {"available": False}
|
|
@@ -1080,9 +1141,25 @@ def _gc(cache_d: Path) -> None:
|
|
|
1080
1141
|
oldest core+snapshot files until under budget.
|
|
1081
1142
|
Views and CAS blobs are swept after each pass.
|
|
1082
1143
|
"""
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1144
|
+
# R-01 (5.9.27): eviction is the one cache operation a concurrent peer can
|
|
1145
|
+
# be hurt by. Writes are unique-temp + `os.replace` and every read is
|
|
1146
|
+
# guarded, so a peer degrades to a cache miss at worst — but `_gc` deletes
|
|
1147
|
+
# the working set a peer may be halfway through reading, turning its warm
|
|
1148
|
+
# run into a cold one for reasons nothing in its payload could name. A
|
|
1149
|
+
# deferred sweep costs disk until the machine is quiet; it is bounded by
|
|
1150
|
+
# the same caps on the next single-tenant run.
|
|
1151
|
+
try:
|
|
1152
|
+
from sourcecode import concurrency as _concurrency
|
|
1153
|
+
|
|
1154
|
+
if _concurrency.peers() > 0:
|
|
1155
|
+
return
|
|
1156
|
+
except Exception:
|
|
1157
|
+
pass
|
|
1158
|
+
|
|
1159
|
+
_contract = eviction_contract()
|
|
1160
|
+
keep = int(_contract["keep_commits"])
|
|
1161
|
+
max_cores = int(_contract["max_cores"])
|
|
1162
|
+
max_size_bytes = int(_contract["max_size_mb"]) * 1024 * 1024
|
|
1086
1163
|
|
|
1087
1164
|
try:
|
|
1088
1165
|
all_snapshots = list(cache_d.glob("snapshot-*.json.gz"))
|
sourcecode/cli.py
CHANGED
|
@@ -2208,6 +2208,10 @@ def _stamp_envelope(content: str) -> str:
|
|
|
2208
2208
|
if _AGENT_MODE_ACTIVE else None
|
|
2209
2209
|
),
|
|
2210
2210
|
)
|
|
2211
|
+
# BUG-22: *when* the ceiling is enforced, not only what it is. Both
|
|
2212
|
+
# 5.9.25 audits measured a peak above a ceiling that was genuinely in
|
|
2213
|
+
# force, because the check ran only between phases.
|
|
2214
|
+
extra["memory"]["enforcement"] = resource_budget.enforcement_disclosure()
|
|
2211
2215
|
except Exception as _exc: # a footprint sample is not the envelope
|
|
2212
2216
|
degraded = degraded or f"memory disclosure unavailable: {type(_exc).__name__}"
|
|
2213
2217
|
|
|
@@ -2227,6 +2231,18 @@ def _stamp_envelope(content: str) -> str:
|
|
|
2227
2231
|
except Exception as _exc:
|
|
2228
2232
|
degraded = degraded or f"budget disclosure unavailable: {type(_exc).__name__}"
|
|
2229
2233
|
|
|
2234
|
+
# R-01 (5.9.27): whether this run had the machine to itself. Four audit
|
|
2235
|
+
# rounds could not separate a contended measurement from an isolated one
|
|
2236
|
+
# because no payload carried the variable; the round that finally found a
|
|
2237
|
+
# foreign `ask` process on the audit machine found it in this product's
|
|
2238
|
+
# own diagnostics file, not in any answer it published.
|
|
2239
|
+
try:
|
|
2240
|
+
from sourcecode import concurrency as _concurrency
|
|
2241
|
+
|
|
2242
|
+
extra["concurrency"] = _concurrency.disclosure()
|
|
2243
|
+
except Exception as _exc:
|
|
2244
|
+
degraded = degraded or f"concurrency disclosure unavailable: {type(_exc).__name__}"
|
|
2245
|
+
|
|
2230
2246
|
# AUD-601-M01: the state a figure was taken in, from the lookups the layers
|
|
2231
2247
|
# actually answered. Four retracted performance findings in six rounds were
|
|
2232
2248
|
# all measured honestly against a cache state nobody could establish; a
|
|
@@ -3130,7 +3146,18 @@ def _analysis_failed(command: str, phase: str, exc: Exception) -> NoReturn:
|
|
|
3130
3146
|
command-specific exception types. These commands are consumed by CI, so an
|
|
3131
3147
|
unexpected analysis exception must retain a machine-readable cause and a
|
|
3132
3148
|
non-zero process status rather than relying on a framework traceback.
|
|
3149
|
+
|
|
3150
|
+
BUG-22 carved out one exception: a run stopped by its own RSS ceiling did
|
|
3151
|
+
not fail — it was refused, deliberately, by a gate the caller configured.
|
|
3152
|
+
Rendering that as `EXECUTION_FAILED: risk failed while composing risk
|
|
3153
|
+
factors` with `exception_type: CeilingExceeded` buries the one fact the
|
|
3154
|
+
operator needs (the ceiling, the peak, the variable that moves it) under a
|
|
3155
|
+
generic crash report.
|
|
3133
3156
|
"""
|
|
3157
|
+
from sourcecode.resource_budget import CeilingExceeded as _CeilingExceeded
|
|
3158
|
+
|
|
3159
|
+
if isinstance(exc, _CeilingExceeded):
|
|
3160
|
+
raise exc
|
|
3134
3161
|
_emit_error_json(
|
|
3135
3162
|
EXECUTION_FAILED_CODE,
|
|
3136
3163
|
f"{command} failed while {phase}.",
|
|
@@ -3639,6 +3666,48 @@ try:
|
|
|
3639
3666
|
return str(getattr(exc, "message", "") or exc.format_message())
|
|
3640
3667
|
return exc.format_message()
|
|
3641
3668
|
|
|
3669
|
+
def _repository_wide_intent_hint(exc: Any) -> Optional[str]:
|
|
3670
|
+
"""`--target` on a `retrieve` intent that has no anchor to give it.
|
|
3671
|
+
|
|
3672
|
+
Four consecutive audit rounds tried `retrieve security-surface --target
|
|
3673
|
+
<symbol>` and got *"No such option: --target"*, which is true and
|
|
3674
|
+
useless: it does not say the intent is repository-wide by construction,
|
|
3675
|
+
and it does not name the sibling intent that answers the question the
|
|
3676
|
+
caller was asking. `intent_router.ANCHOR_KINDS` already holds the fact
|
|
3677
|
+
(`""` means "takes no anchor"), so the hint is derived from it rather
|
|
3678
|
+
than from a second list that would drift the first time an intent
|
|
3679
|
+
changed shape.
|
|
3680
|
+
"""
|
|
3681
|
+
option = str(getattr(exc, "option_name", None) or "").strip()
|
|
3682
|
+
if option not in ("--target", "-t"):
|
|
3683
|
+
return None
|
|
3684
|
+
path = str(getattr(getattr(exc, "ctx", None), "command_path", "")).strip()
|
|
3685
|
+
parts = path.split()
|
|
3686
|
+
if len(parts) < 2 or parts[-2] != "retrieve":
|
|
3687
|
+
return None
|
|
3688
|
+
intent = parts[-1]
|
|
3689
|
+
try:
|
|
3690
|
+
from sourcecode.intent_router import ANCHOR_KINDS
|
|
3691
|
+
except Exception:
|
|
3692
|
+
return None
|
|
3693
|
+
if ANCHOR_KINDS.get(intent, None) != "":
|
|
3694
|
+
return None
|
|
3695
|
+
family = intent.split("-")[0]
|
|
3696
|
+
siblings = sorted(
|
|
3697
|
+
name for name, kind in ANCHOR_KINDS.items()
|
|
3698
|
+
if kind and name.split("-")[0] == family
|
|
3699
|
+
)
|
|
3700
|
+
pointer = (
|
|
3701
|
+
f" For a target-scoped answer in the same family use "
|
|
3702
|
+
f"`ask retrieve {siblings[0]} --target <symbol>`."
|
|
3703
|
+
if siblings else
|
|
3704
|
+
" `ask retrieve --help` lists the intents that take one."
|
|
3705
|
+
)
|
|
3706
|
+
return (
|
|
3707
|
+
f"`retrieve {intent}` is repository-wide and takes no anchor, so it "
|
|
3708
|
+
f"has no `--target` to accept.{pointer}"
|
|
3709
|
+
)
|
|
3710
|
+
|
|
3642
3711
|
def _declared_options_hint(exc: Any) -> Optional[str]:
|
|
3643
3712
|
"""The command's real signature, for a refusal with no candidate.
|
|
3644
3713
|
|
|
@@ -3666,6 +3735,11 @@ try:
|
|
|
3666
3735
|
_is_root_scope = _hint is not None
|
|
3667
3736
|
if _numeric_context:
|
|
3668
3737
|
_flag = str(_numeric_context["flag"])
|
|
3738
|
+
if not _hint:
|
|
3739
|
+
# Before the edit-distance suggester: `--target` on a
|
|
3740
|
+
# repository-wide intent has a real answer, and a near-miss
|
|
3741
|
+
# candidate would bury it.
|
|
3742
|
+
_hint = _repository_wide_intent_hint(self)
|
|
3669
3743
|
if not _hint:
|
|
3670
3744
|
_nearby = _nearby_command_flag_hint(self)
|
|
3671
3745
|
if _nearby:
|
|
@@ -6541,6 +6615,14 @@ def main(
|
|
|
6541
6615
|
return
|
|
6542
6616
|
from sourcecode import resource_budget
|
|
6543
6617
|
|
|
6618
|
+
# BUG-22: `--agent` carries a default ceiling that no environment
|
|
6619
|
+
# variable states, so the mid-phase sampler has to be told about it or
|
|
6620
|
+
# it enforces nothing on exactly the mode that needs it most.
|
|
6621
|
+
resource_budget.arm(
|
|
6622
|
+
"ask --agent",
|
|
6623
|
+
default_ceiling_mb=resource_budget.DEFAULT_AGENT_RSS_CEILING_MB,
|
|
6624
|
+
)
|
|
6625
|
+
|
|
6544
6626
|
memory_error = resource_budget.exceeded(
|
|
6545
6627
|
"ask --agent",
|
|
6546
6628
|
default_ceiling_mb=resource_budget.DEFAULT_AGENT_RSS_CEILING_MB,
|
|
@@ -18660,6 +18742,12 @@ def selftest_cmd(
|
|
|
18660
18742
|
output_path: Optional[Path] = typer.Option(
|
|
18661
18743
|
None, "--output", "-o", help="Write the verdicts to a file instead of stdout.",
|
|
18662
18744
|
),
|
|
18745
|
+
allow_nested: bool = typer.Option(
|
|
18746
|
+
False, "--allow-nested",
|
|
18747
|
+
help="Run anyway when the implicit `.` covers nested Git repositories "
|
|
18748
|
+
"(BUG-13). The default refuses: an unscoped ledger run over a "
|
|
18749
|
+
"workspace of repositories is not the run anyone asked for.",
|
|
18750
|
+
),
|
|
18663
18751
|
format: str = _format_option(help="Output format: json (default) or yaml."),
|
|
18664
18752
|
copy: bool = _copy_option(),
|
|
18665
18753
|
progress_mode: Optional[str] = _progress_option(),
|
|
@@ -18696,13 +18784,27 @@ def selftest_cmd(
|
|
|
18696
18784
|
from sourcecode.selftest import nested_repository_count
|
|
18697
18785
|
|
|
18698
18786
|
nested = nested_repository_count(target)
|
|
18699
|
-
if nested:
|
|
18700
|
-
|
|
18701
|
-
|
|
18702
|
-
|
|
18703
|
-
|
|
18704
|
-
|
|
18787
|
+
if nested and not allow_nested:
|
|
18788
|
+
# BUG-13: this used to warn and then scan anyway. A warning on
|
|
18789
|
+
# stderr does not stop the run, and what the run then does is
|
|
18790
|
+
# drive every ledger row against a *workspace* — the shape behind
|
|
18791
|
+
# `selftest` falling from 9/12 to 1/12 rows inside one 120-second
|
|
18792
|
+
# deadline on the audited machine. The scope is the input, so the
|
|
18793
|
+
# input is what has to change; `--allow-nested` is there for a
|
|
18794
|
+
# caller who means it.
|
|
18795
|
+
_emit_error_json(
|
|
18796
|
+
INVALID_USAGE_CODE,
|
|
18797
|
+
f"selftest refuses an unscoped run: the implicit `.` covers "
|
|
18798
|
+
f"{nested} nested Git repository(s), so the ledger would run "
|
|
18799
|
+
f"against a workspace rather than a repository.",
|
|
18800
|
+
hint=(
|
|
18801
|
+
"Pass the repository explicitly (`ask selftest "
|
|
18802
|
+
"<path/to/repo>`), or `--allow-nested` to run over the "
|
|
18803
|
+
"workspace on purpose."
|
|
18804
|
+
),
|
|
18805
|
+
nested_repositories=nested,
|
|
18705
18806
|
)
|
|
18807
|
+
raise typer.Exit(code=2)
|
|
18706
18808
|
_enforce_format("selftest", format)
|
|
18707
18809
|
selected = tuple(part.strip() for part in (rows or "").split(",") if part.strip())
|
|
18708
18810
|
|
|
@@ -20622,6 +20724,11 @@ def cache_status_cmd(
|
|
|
20622
20724
|
f"Cache dir: {stats['cache_dir']}"
|
|
20623
20725
|
+ (f" (${_core_env} moves it)" if _core_env else "")
|
|
20624
20726
|
)
|
|
20727
|
+
# BUG-16: the per-repository store's own eviction contract, beside the
|
|
20728
|
+
# directory it applies to.
|
|
20729
|
+
_core_eviction = ((stats.get("stores") or {}).get("core") or {}).get("eviction")
|
|
20730
|
+
if isinstance(_core_eviction, dict) and _core_eviction.get("policy"):
|
|
20731
|
+
_answer.say(f"{'':<13}eviction: {_core_eviction['policy']}")
|
|
20625
20732
|
# C4-18: this counts cached core entries, not CPU cores — and `cores` is
|
|
20626
20733
|
# exactly what a captured baseline calls the machine's CPU count, so
|
|
20627
20734
|
# `Cores: 0` beside `Views: 0` read as "empty cache" on a 20-core box.
|
|
@@ -20662,6 +20769,20 @@ def cache_status_cmd(
|
|
|
20662
20769
|
"written by another build and can never be read again — the next "
|
|
20663
20770
|
"analysis retires them"
|
|
20664
20771
|
)
|
|
20772
|
+
# BUG-16: what evicts this store, and when. The context cache was
|
|
20773
|
+
# measured at ~1.7 GB across two rounds with its caps visible only
|
|
20774
|
+
# inside the sweep that applied them; every store that has an
|
|
20775
|
+
# eviction contract now states it here, from the store's own
|
|
20776
|
+
# figures rather than a second copy of them.
|
|
20777
|
+
_eviction = _store.get("eviction")
|
|
20778
|
+
if (
|
|
20779
|
+
isinstance(_eviction, dict)
|
|
20780
|
+
and _eviction.get("policy")
|
|
20781
|
+
and "budget_bytes" not in _store
|
|
20782
|
+
):
|
|
20783
|
+
# A store that publishes a byte budget states its policy in the
|
|
20784
|
+
# budget line below; saying it twice is two catalogues again.
|
|
20785
|
+
_answer.say(f"{'':<13}eviction: {_eviction['policy']}")
|
|
20665
20786
|
# F-AR: a store in somebody else's home directory says what bounds it.
|
|
20666
20787
|
if "budget_bytes" in _store:
|
|
20667
20788
|
_budget_mb = _store.get("budget_mb")
|
|
@@ -21704,12 +21825,33 @@ def main_entry() -> None:
|
|
|
21704
21825
|
if any(tok in REQUIRED_ANSWER_COMMANDS for tok in sys.argv[1:]):
|
|
21705
21826
|
from sourcecode import empty_answer_diagnostics as _diag
|
|
21706
21827
|
_diag.record_entry_reached(sys.argv[1:])
|
|
21828
|
+
# R-01 (5.9.27 alternative audit): announce this invocation on the machine
|
|
21829
|
+
# before any work, so a peer that starts later sizes its worker pool
|
|
21830
|
+
# against a census that includes this run — and so this run's own payload
|
|
21831
|
+
# can say whether it was sharing the machine. Best-effort: a failure here
|
|
21832
|
+
# leaves the pre-5.9.28 behaviour untouched.
|
|
21833
|
+
try:
|
|
21834
|
+
from sourcecode import concurrency as _concurrency
|
|
21835
|
+
|
|
21836
|
+
_concurrency.register(sys.argv[1:])
|
|
21837
|
+
except Exception:
|
|
21838
|
+
pass
|
|
21707
21839
|
# AUD-596-B10: the earliest point in the process that knows a command is
|
|
21708
21840
|
# starting. `timings.wall_ms` is published under a unit reading "THIS
|
|
21709
21841
|
# process's wall clock", and until this mark existed it measured from
|
|
21710
21842
|
# wherever the block happened to be constructed — which, on a run the budget
|
|
21711
21843
|
# cut, is after the work. Marked before anything else so the number covers
|
|
21712
21844
|
# what the reader was charged for.
|
|
21845
|
+
# BUG-22: arm mid-phase RSS enforcement for the whole process. Without a
|
|
21846
|
+
# configured ceiling this is inert (`armed()` is false and `checkpoint()`
|
|
21847
|
+
# returns immediately); with one, the analysis loops stop where the
|
|
21848
|
+
# crossing happens instead of at the end of the phase that caused it.
|
|
21849
|
+
try:
|
|
21850
|
+
from sourcecode import resource_budget as _rb_arm
|
|
21851
|
+
|
|
21852
|
+
_rb_arm.arm(" ".join(["ask", *sys.argv[1:2]]).strip())
|
|
21853
|
+
except Exception:
|
|
21854
|
+
pass
|
|
21713
21855
|
from sourcecode.perf import mark_command_start as _mark_command_start
|
|
21714
21856
|
_mark_command_start()
|
|
21715
21857
|
_force_utf8_streams()
|
|
@@ -21753,6 +21895,7 @@ def main_entry() -> None:
|
|
|
21753
21895
|
raise
|
|
21754
21896
|
except typer.Exit as _exit:
|
|
21755
21897
|
raise SystemExit(_exit.exit_code)
|
|
21898
|
+
from sourcecode import resource_budget as _resource_budget
|
|
21756
21899
|
try:
|
|
21757
21900
|
# prog_name pins usage/help to the canonical `ask`, whatever the alias.
|
|
21758
21901
|
app(prog_name="ask")
|
|
@@ -21762,6 +21905,18 @@ def main_entry() -> None:
|
|
|
21762
21905
|
_fail_on_missing_required_answer(_sysexit.code)
|
|
21763
21906
|
_fail_on_unkept_output_promise(_sysexit.code)
|
|
21764
21907
|
raise
|
|
21908
|
+
except _resource_budget.CeilingExceeded as _ceiling:
|
|
21909
|
+
# BUG-22: the same refusal `_root_memory_seam` emits at a boundary,
|
|
21910
|
+
# raised from inside the loop that crossed the ceiling. One envelope,
|
|
21911
|
+
# one exit code, whichever seam noticed.
|
|
21912
|
+
_payload = dict(_ceiling.payload)
|
|
21913
|
+
_fail_on_missing_required_answer(1)
|
|
21914
|
+
_emit_error_json(
|
|
21915
|
+
_payload.pop("code"),
|
|
21916
|
+
_payload.pop("message"),
|
|
21917
|
+
**_payload,
|
|
21918
|
+
)
|
|
21919
|
+
raise SystemExit(1)
|
|
21765
21920
|
except WriteRefused as _refused:
|
|
21766
21921
|
# F-AS. "A command that cannot answer without writing says so instead of
|
|
21767
21922
|
# writing" — and *says so* means one sentence in the error envelope every
|