sourcecode 4.18.0__py3-none-any.whl → 5.0.1__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/cache_model.py +329 -43
- sourcecode/cli.py +20 -4
- sourcecode/detach.py +7 -3
- sourcecode/execution_plan.py +76 -21
- sourcecode/perf.py +4 -2
- sourcecode/phased_run.py +10 -8
- sourcecode/posture.py +13 -26
- sourcecode/release_info.py +1 -1
- sourcecode/repository_ir.py +21 -31
- sourcecode/security_chain.py +148 -0
- sourcecode/security_config_scan.py +54 -4
- sourcecode/source_text.py +18 -0
- {sourcecode-4.18.0.dist-info → sourcecode-5.0.1.dist-info}/METADATA +5 -5
- {sourcecode-4.18.0.dist-info → sourcecode-5.0.1.dist-info}/RECORD +18 -17
- {sourcecode-4.18.0.dist-info → sourcecode-5.0.1.dist-info}/WHEEL +0 -0
- {sourcecode-4.18.0.dist-info → sourcecode-5.0.1.dist-info}/entry_points.txt +0 -0
- {sourcecode-4.18.0.dist-info → sourcecode-5.0.1.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cache_model.py
CHANGED
|
@@ -74,11 +74,11 @@ class CommandCache:
|
|
|
74
74
|
cold_seconds: "Optional[float]" = None
|
|
75
75
|
warm_seconds: "Optional[float]" = None
|
|
76
76
|
#: The other measured point: this command on the field repository
|
|
77
|
-
#: (3 342 Java files, Windows 11 / PowerShell 5.1 / pipx,
|
|
78
|
-
#: evaluations
|
|
79
|
-
#:
|
|
80
|
-
#:
|
|
81
|
-
#:
|
|
77
|
+
#: (3 342 Java files, Windows 11 / PowerShell 5.1 / pipx, warm), from the
|
|
78
|
+
#: field evaluations. Seconds where a run finished; `field_blocked` where it
|
|
79
|
+
#: did not — the harness promoted the process and the session died, which is a
|
|
80
|
+
#: measurement of a different kind and is never written as a duration. Rows
|
|
81
|
+
#: with neither were not attempted there.
|
|
82
82
|
#:
|
|
83
83
|
#: C3-72: where two evaluations of the same command at this size disagree, the
|
|
84
84
|
#: figure here is the **most recent** one, because it describes the build a
|
|
@@ -87,7 +87,39 @@ class CommandCache:
|
|
|
87
87
|
#: releases (spring-audit, 408 s in #13 against 2 351 s in #16) is itself the
|
|
88
88
|
#: measurement C3-76 exists for.
|
|
89
89
|
field_seconds: "Optional[float]" = None
|
|
90
|
+
#: **C3-100.** `field_blocked` is a *measurement outcome*, not a property of the
|
|
91
|
+
#: command: it says "this run did not finish on that build, at that size", and
|
|
92
|
+
#: like every other measurement here it belongs to the build named in
|
|
93
|
+
#: `field_measured_version` and expires with it. It was read as a property for
|
|
94
|
+
#: three releases — `repo-ir` and `verify-edit` carried a 4.10.4 outcome while
|
|
95
|
+
#: eval #23 ran them in 9,8 s and 76,6 s — which is worse than a stale number,
|
|
96
|
+
#: because a stale number is a measurement and *"the field could not run this"*
|
|
97
|
+
#: is an incapacity the product does not have. A blocked row without a build is
|
|
98
|
+
#: rejected by the battery for the same reason a figure without one is.
|
|
90
99
|
field_blocked: bool = False
|
|
100
|
+
#: The cache state the field figure was taken in. Eval #22 measured a warm
|
|
101
|
+
#: machine; eval #23 purged every cache first (`cache clear --all`,
|
|
102
|
+
#: `context-clear`, `rm -rf parse-cache-v1`) and then ran the whole catalogue
|
|
103
|
+
#: in one round, so its first figures are cold and its later ones are neither —
|
|
104
|
+
#: the shared layers filled as the round went on. Published because *72,3 s
|
|
105
|
+
#: cold* and *72,3 s warm* are two different facts about the same command, and
|
|
106
|
+
#: the row that says which is the only thing standing between the two:
|
|
107
|
+
#: ``warm`` — the caches were warmed before the run
|
|
108
|
+
#: ``cold`` — every cache was purged before the run
|
|
109
|
+
#: ``mixed`` — inside a round that began purged: earlier commands had already
|
|
110
|
+
#: filled some of the shared layers this one reads
|
|
111
|
+
field_cache_state: str = "warm"
|
|
112
|
+
#: **C3-97.** The build the field figure was taken on. An anchor without one is
|
|
113
|
+
#: a measurement that can outlive its build and keep deciding: 4.18.0 deleted
|
|
114
|
+
#: two catastrophic-backtracking patterns (C3-95, C3-96) and every figure taken
|
|
115
|
+
#: before it describes a cost that no longer exists — `spring-audit` at 2 351 s
|
|
116
|
+
#: where the same command on the same commit of the same repository now takes
|
|
117
|
+
#: 8,4 s. Anchors older than the running build are still published, because a
|
|
118
|
+
#: measurement is worth more than silence; what changes is that they are
|
|
119
|
+
#: **labelled** with the build they belong to, so a reader can see the figure is
|
|
120
|
+
#: not this one's. `field_anchor_note()` renders that label and
|
|
121
|
+
#: `execution_plan` appends it to the `here:` line.
|
|
122
|
+
field_measured_version: "Optional[str]" = None
|
|
91
123
|
|
|
92
124
|
|
|
93
125
|
#: Where every `measured` figure comes from.
|
|
@@ -109,26 +141,37 @@ REFERENCE_JAVA_FILES = 2000
|
|
|
109
141
|
REFERENCE_MEASURED_VERSION = "3.2.2"
|
|
110
142
|
|
|
111
143
|
#: The other end of the measured range, and the reason this module publishes a
|
|
112
|
-
#: *class* rather than a projected duration (C4-19). Field evaluation #
|
|
144
|
+
#: *class* rather than a projected duration (C4-19). Field evaluation #22, on
|
|
113
145
|
#: Windows 11 / PowerShell 5.1 / pipx / Python 3.10: `spring-audit` on a
|
|
114
|
-
#: 3 342-file repository ran **
|
|
115
|
-
#:
|
|
116
|
-
#: scale with file count in any way this product has measured, so a projected
|
|
117
|
-
#: "~15 s on your repository" would be a confident falsehood of exactly the kind
|
|
118
|
-
#: the ledger exists to prevent. What can be said honestly is the class, the two
|
|
119
|
-
#: measured anchors, and how to run it.
|
|
146
|
+
#: 3 342-file repository ran **8,4 s** — the same command takes 8.8 s on the
|
|
147
|
+
#: 2 000-file reference, for a repository 1,7× the size.
|
|
120
148
|
#:
|
|
121
|
-
#:
|
|
122
|
-
#:
|
|
123
|
-
#:
|
|
124
|
-
#:
|
|
125
|
-
#:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
149
|
+
#: **The refusal to project stands, and this evaluation strengthened it** (C3-97).
|
|
150
|
+
#: For eight releases the pair of anchors said *cost does not track file count*;
|
|
151
|
+
#: what the series actually shows is that **cost tracks the build**. The identical
|
|
152
|
+
#: command on the identical commit of the identical repository measured 2 557 s
|
|
153
|
+
#: (4.10.6), 2 398 s (4.10.7), 2 351 s (4.11.0), 2 377 s (4.12.0), 1 371 s
|
|
154
|
+
#: (4.16.0) and 8,4 s (4.18.0) — a 280× move produced by deleting two
|
|
155
|
+
#: catastrophic-backtracking patterns (C3-95, C3-96), with every payload
|
|
156
|
+
#: byte-identical throughout. A projection would therefore have to predict our own
|
|
157
|
+
#: next release, which is the confident falsehood the ledger exists to prevent.
|
|
158
|
+
#: What can be said honestly is the class, the two measured anchors, **the build
|
|
159
|
+
#: each was measured on**, and how to run it.
|
|
160
|
+
#:
|
|
161
|
+
#: History of the anchor, kept because it is the measurement C3-76 exists for:
|
|
162
|
+
#: 408 s (#13) → 2 351 s (#16, C3-72) → 8,4 s (#22, C3-97). The middle figure was
|
|
163
|
+
#: quoted as an operational anchor for four releases after the build that produced
|
|
164
|
+
#: it, and told operators to detach commands that finish in seconds.
|
|
165
|
+
#:
|
|
166
|
+
#: ⚠ `FIELD_ANCHOR`, `FIELD_ANCHOR_SECONDS` and `FIELD_ANCHOR_MEASURED_VERSION`
|
|
167
|
+
#: are **derived from the `spring-audit` row**, below `COMMANDS`. They used to be
|
|
168
|
+
#: written out here as well, which made the anchor two facts kept in step by hand:
|
|
169
|
+
#: C3-97 refreshed both, and C3-100 is what it costs when the next refresh reaches
|
|
170
|
+
#: only one of the copies. One authority per fact — the table — and the headline
|
|
171
|
+
#: reads itself off it.
|
|
172
|
+
#:
|
|
173
|
+
#: The size of the field repository every `field_seconds` below was taken on.
|
|
130
174
|
FIELD_ANCHOR_JAVA_FILES = 3342
|
|
131
|
-
FIELD_ANCHOR_SECONDS = 2351.4
|
|
132
175
|
|
|
133
176
|
|
|
134
177
|
#: Every layer keys on `cache.worktree_signature` — the exact tree state (C1-9).
|
|
@@ -194,14 +237,17 @@ COMMANDS: tuple[CommandCache, ...] = (
|
|
|
194
237
|
CommandCache("ask (root)", ("snapshot", "ris", "parse"), "answer", True,
|
|
195
238
|
"`--compact` is what a warm stores by default; `--agent` needs `cache warm --agent`. "
|
|
196
239
|
"`--env-map`, `--depth N` and `--exclude` change the *analysis*, so they miss the "
|
|
197
|
-
"warmed core and rescan —
|
|
240
|
+
"warmed core and rescan — the 171 s the field measured after a 103 s warm on 4.10.4. "
|
|
241
|
+
"Eval #23 timed the same invocation at 72,3 s from a purged cache, and then paid "
|
|
242
|
+
"71,7 s again for `--agent` on the state it had just analysed (C3-103).",
|
|
198
243
|
"--compact 13.3 s cold → 0.3 s warm (cold re-measured on 3.7.0: was 19.3 s, C3-6); "
|
|
199
|
-
"--agent --full --env-map --depth 20 34.7 s → 33.9 s (no gain)", analysis_class="repo-wide", cold_seconds=13.3, warm_seconds=0.3,
|
|
244
|
+
"--agent --full --env-map --depth 20 34.7 s → 33.9 s (no gain)", analysis_class="repo-wide", cold_seconds=13.3, warm_seconds=0.3,
|
|
245
|
+
field_seconds=72.3, field_cache_state="cold", field_measured_version="5.0.0"),
|
|
200
246
|
CommandCache("posture", ("cir", "parse"), "shared", False,
|
|
201
247
|
"Resolves the conditional bean graph on every run, over the shared CIR a warm "
|
|
202
248
|
"builds — the parse it used to repeat for itself. `--diff` compares two profile "
|
|
203
249
|
"sets over that one IR, so the second side costs the resolution only.",
|
|
204
|
-
"10.1 s → 1.6 s", analysis_class="repo-wide", cold_seconds=10.1, warm_seconds=1.6, field_seconds=
|
|
250
|
+
"10.1 s → 1.6 s", analysis_class="repo-wide", cold_seconds=10.1, warm_seconds=1.6, field_seconds=8.0, field_measured_version="4.18.0"),
|
|
205
251
|
CommandCache("risk", ("cir", "parse"), "shared", False,
|
|
206
252
|
"Composes what the audit, impact-chain and the posture already answer, so it "
|
|
207
253
|
"pays each of their costs once over the shared CIR a warm builds — one parse "
|
|
@@ -209,7 +255,7 @@ COMMANDS: tuple[CommandCache, ...] = (
|
|
|
209
255
|
"within the run.",
|
|
210
256
|
"not measured on the battery yet — the composition is bounded by the "
|
|
211
257
|
"`spring-audit` + `impact-chain` costs listed here, not by new analysis",
|
|
212
|
-
analysis_class="deep", field_seconds=
|
|
258
|
+
analysis_class="deep", field_seconds=73.3, field_measured_version="4.18.0"),
|
|
213
259
|
CommandCache("enrich", ("cir", "parse"), "shared", False,
|
|
214
260
|
"Runs the same composition as `risk` over the repository, then joins a SARIF "
|
|
215
261
|
"log to it. Reading the log is negligible; everything a warm helps with is the "
|
|
@@ -239,25 +285,33 @@ COMMANDS: tuple[CommandCache, ...] = (
|
|
|
239
285
|
"Recomputes the endpoint surface on every run, over a parse a warm has already "
|
|
240
286
|
"paid for. Until 3.7.0 the extractor parsed every file itself instead of reading "
|
|
241
287
|
"the shared parse cache, and a warm measurably bought it nothing (C3-6).",
|
|
242
|
-
"3.3 s → 1.4 s (re-measured on 3.7.0; was 2.8 s → 2.9 s)", analysis_class="repo-wide", cold_seconds=3.3, warm_seconds=1.4, field_seconds=
|
|
288
|
+
"3.3 s → 1.4 s (re-measured on 3.7.0; was 2.8 s → 2.9 s)", analysis_class="repo-wide", cold_seconds=3.3, warm_seconds=1.4, field_seconds=3.8, field_measured_version="4.18.0"),
|
|
243
289
|
CommandCache("spring-audit", ("ris", "parse"), "shared", False,
|
|
244
290
|
"Recomputes every run, but over a parse a warm has already paid for.",
|
|
245
|
-
"8.8 s → 3.7 s", analysis_class="repo-wide", cold_seconds=8.8, warm_seconds=3.7, field_seconds=
|
|
291
|
+
"8.8 s → 3.7 s", analysis_class="repo-wide", cold_seconds=8.8, warm_seconds=3.7, field_seconds=8.4, field_measured_version="4.18.0"),
|
|
246
292
|
CommandCache("migrate-check", ("cir",), "none", False,
|
|
247
293
|
"Computes its own inventory and shares nothing a warm builds. Only `--blast-radius` "
|
|
248
294
|
"reuses the shared CIR.",
|
|
249
|
-
"4.8 s → 4.8 s", analysis_class="repo-wide", cold_seconds=4.8, warm_seconds=4.8, field_seconds=
|
|
295
|
+
"4.8 s → 4.8 s", analysis_class="repo-wide", cold_seconds=4.8, warm_seconds=4.8, field_seconds=10.2, field_measured_version="4.18.0"),
|
|
250
296
|
CommandCache("impact-chain", ("cir", "parse"), "shared", False,
|
|
251
297
|
"The CIR is the expensive half — this is where a warm pays most.",
|
|
252
|
-
"9.9 s → 1.7 s", analysis_class="core", cold_seconds=9.9, warm_seconds=1.7
|
|
253
|
-
|
|
298
|
+
"9.9 s → 1.7 s", analysis_class="core", cold_seconds=9.9, warm_seconds=1.7,
|
|
299
|
+
field_seconds=5.5, field_measured_version="4.18.0"),
|
|
300
|
+
CommandCache("impact", ("parse",), "shared", False, "", "4.9 s → 2.8 s", analysis_class="core", cold_seconds=4.9, warm_seconds=2.8,
|
|
301
|
+
field_seconds=7.7, field_measured_version="4.18.0"),
|
|
254
302
|
CommandCache("pr-impact", ("parse",), "shared", False,
|
|
255
|
-
"Diff-dependent: the answer itself is never stored.
|
|
303
|
+
"Diff-dependent: the answer itself is never stored. What it costs follows the "
|
|
304
|
+
"diff, not the repository: 12,5 s on an ordinary one and 11,8 s on a diff of "
|
|
305
|
+
"security configuration, measured at field scale.", analysis_class="core",
|
|
306
|
+
field_seconds=12.5, field_measured_version="4.18.0"),
|
|
256
307
|
CommandCache("verify", (), "none", False, "Runs the contracts against a fresh reading.", analysis_class="core"),
|
|
257
308
|
CommandCache("verify-edit", ("parse",), "shared", True,
|
|
258
309
|
"Built for the edit loop: the parse cache is what keeps an unchanged file out of the "
|
|
259
|
-
"next run. Its own second run is faster again."
|
|
260
|
-
"
|
|
310
|
+
"next run. Its own second run is faster again. At field size that loop is not short "
|
|
311
|
+
"yet: eval #23 measured 76,6 s on a tree with no edits, because the HEAD side is "
|
|
312
|
+
"still built in a throwaway worktree instead of reusing the shared CIR (C3-102).",
|
|
313
|
+
"14.6 s → 9.6 s → 5.6 s on repeat", analysis_class="repo-wide", cold_seconds=14.6, warm_seconds=9.6,
|
|
314
|
+
field_seconds=76.6, field_cache_state="mixed", field_measured_version="5.0.0"),
|
|
261
315
|
CommandCache("review-pr", ("cir",), "none", False,
|
|
262
316
|
"Diff-dependent, and it reuses the CIR only if one exists. On a small diff, loading "
|
|
263
317
|
"the warmed CIR costs more than the work it saves.",
|
|
@@ -286,19 +340,33 @@ COMMANDS: tuple[CommandCache, ...] = (
|
|
|
286
340
|
"WITHOUT a warm, a second identical run: 10.2 s → 9.4 s (5 486 files) — "
|
|
287
341
|
"no answer hit", analysis_class="repo-wide", cold_seconds=6.5, warm_seconds=0.3),
|
|
288
342
|
CommandCache("explain", ("cir",), "shared", False, "Serves from the shared CIR a warm builds.",
|
|
289
|
-
"9.8 s → 1.6 s", analysis_class="core", cold_seconds=9.8, warm_seconds=1.6
|
|
343
|
+
"9.8 s → 1.6 s", analysis_class="core", cold_seconds=9.8, warm_seconds=1.6,
|
|
344
|
+
field_seconds=4.4, field_measured_version="4.18.0"),
|
|
290
345
|
CommandCache("export", ("parse",), "shared", False, "", "8.8 s → 3.7 s", analysis_class="repo-wide", cold_seconds=8.8, warm_seconds=3.7),
|
|
291
|
-
CommandCache("repo-ir", ("parse",), "shared", False,
|
|
292
|
-
|
|
293
|
-
|
|
346
|
+
CommandCache("repo-ir", ("parse",), "shared", False,
|
|
347
|
+
"Carried as *did not finish* from 4.10.4 until eval #23 ran it at field size in "
|
|
348
|
+
"9,8 s — a run that did not finish once is not a command that cannot finish "
|
|
349
|
+
"(C3-100, and the same correction `modernize` needed).",
|
|
350
|
+
"5.1 s → 2.9 s", analysis_class="repo-wide", cold_seconds=5.1, warm_seconds=2.9,
|
|
351
|
+
field_seconds=9.8, field_cache_state="mixed", field_measured_version="5.0.0"),
|
|
352
|
+
CommandCache("validation", ("parse",), "shared", False, "", "11.6 s → 6.4 s", analysis_class="repo-wide", cold_seconds=11.6, warm_seconds=6.4,
|
|
353
|
+
field_seconds=17.4, field_measured_version="4.18.0"),
|
|
354
|
+
CommandCache("modernize", ("parse",), "shared", False,
|
|
355
|
+
"Blocked in the session that recorded C3-53 and measured since: a run that did "
|
|
356
|
+
"not finish once is not a command that cannot finish.",
|
|
357
|
+
"5.2 s → 3.0 s", analysis_class="repo-wide", cold_seconds=5.2, warm_seconds=3.0,
|
|
358
|
+
field_seconds=9.1, field_measured_version="4.18.0"),
|
|
294
359
|
CommandCache("chunk-file", (), "none", False, "Reads one file; nothing to cache.", analysis_class="core"),
|
|
295
360
|
CommandCache("cold-start", ("ris",), "answer", True,
|
|
296
361
|
"Reads the RIS a warm rebuilds — that is all it does. Without one it answers "
|
|
297
362
|
"`no_ris` instead of a snapshot.",
|
|
298
363
|
"0.2 s either way", analysis_class="core", cold_seconds=0.2, warm_seconds=0.2),
|
|
299
364
|
CommandCache("trend", (), "none", False, "Reads stored baseline artifacts from disk; analyses no source, so no cache layer applies. Same command as `baseline trend`.", analysis_class="none"),
|
|
300
|
-
CommandCache("baseline", ("parse",), "shared", False,
|
|
301
|
-
"capture
|
|
365
|
+
CommandCache("baseline", ("parse",), "shared", False,
|
|
366
|
+
"`capture`/`diff`/`trend` over architectural metrics. The field figure is `capture`, "
|
|
367
|
+
"the subcommand that analyses; `trend` reads stored artifacts and has its own row.",
|
|
368
|
+
"capture 8.7 s → 3.6 s", analysis_class="repo-wide", cold_seconds=8.7, warm_seconds=3.6,
|
|
369
|
+
field_seconds=2.2, field_cache_state="mixed", field_measured_version="5.0.0"),
|
|
302
370
|
CommandCache("retrieve", ("cir", "parse"), "shared", False,
|
|
303
371
|
"Every query builds or reuses the shared CIR a warm builds.", analysis_class="core"),
|
|
304
372
|
CommandCache("archetype", ("parse",), "shared", False, "", "9.3 s → 5.6 s", analysis_class="repo-wide", cold_seconds=9.3, warm_seconds=5.6),
|
|
@@ -319,6 +387,73 @@ _WARM_LABEL = {
|
|
|
319
387
|
"none": "nothing",
|
|
320
388
|
}
|
|
321
389
|
|
|
390
|
+
#: The cache states a field figure may declare. A row outside this set is a
|
|
391
|
+
#: measurement whose conditions nobody can read (C3-100).
|
|
392
|
+
FIELD_CACHE_STATES = ("warm", "cold", "mixed")
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def _thousands(n: int) -> str:
|
|
396
|
+
return f"{n:,}".replace(",", " ")
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _row(command: str) -> "CommandCache":
|
|
400
|
+
for entry in COMMANDS:
|
|
401
|
+
if entry.command == command:
|
|
402
|
+
return entry
|
|
403
|
+
raise KeyError(command)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
#: The headline anchor, read off the table rather than written twice (C3-100).
|
|
407
|
+
#: `spring-audit` is the command the whole series measured — 408 s (#13), 2 351 s
|
|
408
|
+
#: (#16), 8,4 s (#22) — so it is the row the front page quotes, and quoting it
|
|
409
|
+
#: means reading it, not copying it.
|
|
410
|
+
_FIELD_ANCHOR_ROW = _row("spring-audit")
|
|
411
|
+
FIELD_ANCHOR_SECONDS = _FIELD_ANCHOR_ROW.field_seconds
|
|
412
|
+
FIELD_ANCHOR_MEASURED_VERSION = _FIELD_ANCHOR_ROW.field_measured_version
|
|
413
|
+
FIELD_ANCHOR = (
|
|
414
|
+
f"field evaluation #22: spring-audit on {_thousands(FIELD_ANCHOR_JAVA_FILES)} "
|
|
415
|
+
f"Java files took {FIELD_ANCHOR_SECONDS:g} s (Windows, pipx, "
|
|
416
|
+
f"{_FIELD_ANCHOR_ROW.field_cache_state} cache) against "
|
|
417
|
+
f"{_FIELD_ANCHOR_ROW.cold_seconds:g} s on the "
|
|
418
|
+
f"{_thousands(REFERENCE_JAVA_FILES)}-file reference"
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def _version_key(version: "Optional[str]") -> tuple:
|
|
423
|
+
"""A comparable key for a release name, for ordering measurements only.
|
|
424
|
+
|
|
425
|
+
Not a distance — `reference_currency` refuses to invent one and this does not
|
|
426
|
+
either. It answers exactly one question: is this row's build older than the
|
|
427
|
+
newest build anything in the table was measured on (C3-100)? Anything
|
|
428
|
+
unparseable sorts oldest, which is the safe direction: it gets labelled.
|
|
429
|
+
"""
|
|
430
|
+
if not version:
|
|
431
|
+
return ()
|
|
432
|
+
parts: list[int] = []
|
|
433
|
+
for chunk in version.split("."):
|
|
434
|
+
digits = "".join(c for c in chunk if c.isdigit())
|
|
435
|
+
if not digits:
|
|
436
|
+
break
|
|
437
|
+
parts.append(int(digits))
|
|
438
|
+
return tuple(parts)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def newest_field_measurement_version() -> "Optional[str]":
|
|
442
|
+
"""The most recent build any field figure in the table was taken on.
|
|
443
|
+
|
|
444
|
+
The reference point for *"this row is older than the table it sits in"* — the
|
|
445
|
+
state C3-100 lived in for three releases, where a refresh reached five rows
|
|
446
|
+
and left four behind on a build four releases older, with nothing anywhere
|
|
447
|
+
saying so.
|
|
448
|
+
"""
|
|
449
|
+
versions = [
|
|
450
|
+
row.field_measured_version
|
|
451
|
+
for row in COMMANDS
|
|
452
|
+
if (row.field_seconds is not None or row.field_blocked)
|
|
453
|
+
and row.field_measured_version
|
|
454
|
+
]
|
|
455
|
+
return max(versions, key=_version_key) if versions else None
|
|
456
|
+
|
|
322
457
|
|
|
323
458
|
def layer(layer_id: str) -> Layer:
|
|
324
459
|
for entry in LAYERS:
|
|
@@ -363,12 +498,124 @@ class Conditioning:
|
|
|
363
498
|
return (
|
|
364
499
|
f"This repository ({self.scope}) is larger than the repository the "
|
|
365
500
|
f"figures below were measured on ({REFERENCE_JAVA_FILES} Java files), "
|
|
366
|
-
f"and cost
|
|
501
|
+
f"and what cost tracks here is the build rather than the file count: "
|
|
367
502
|
f"{FIELD_ANCHOR}. Read each figure as the reference's, not as yours; "
|
|
368
503
|
f"the `here:` lines say how each command can be run on this one."
|
|
369
504
|
)
|
|
370
505
|
|
|
371
506
|
|
|
507
|
+
def field_anchor_note(row: "Optional[CommandCache]") -> "Optional[str]":
|
|
508
|
+
"""The build a row's field figure belongs to, when it is not this one (C3-97).
|
|
509
|
+
|
|
510
|
+
``None`` when there is nothing to qualify — no field figure, or one measured on
|
|
511
|
+
the running build. Otherwise a short parenthetical naming the build, because a
|
|
512
|
+
figure carrying another release's name is a measurement and a figure carrying
|
|
513
|
+
none is a claim about this one.
|
|
514
|
+
|
|
515
|
+
The figure is never suppressed. A measurement from four releases ago still
|
|
516
|
+
beats silence for a reader deciding how to run a command; what it must not do
|
|
517
|
+
is arrive undated, which is how `spring-audit`'s 2 351 s went on recommending
|
|
518
|
+
a nightly job for a command that had come to take 8,4 s.
|
|
519
|
+
"""
|
|
520
|
+
if row is None or (row.field_seconds is None and not row.field_blocked):
|
|
521
|
+
return None
|
|
522
|
+
from sourcecode import __version__
|
|
523
|
+
|
|
524
|
+
measured_on = row.field_measured_version
|
|
525
|
+
if measured_on is None:
|
|
526
|
+
return "build not recorded"
|
|
527
|
+
if measured_on == __version__:
|
|
528
|
+
return None
|
|
529
|
+
# Short on purpose: this rides along every `here:` line and every row of
|
|
530
|
+
# `cache model`, and the build is the whole fact. The sentence that explains
|
|
531
|
+
# what an older build means is `field_currency()['statement']`, printed once.
|
|
532
|
+
return f"on {measured_on}"
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def cost_sentence() -> str:
|
|
536
|
+
"""The one paragraph the front page says about cost, derived here (CL-21).
|
|
537
|
+
|
|
538
|
+
`--help` and the README used to carry their own hand-written version of it —
|
|
539
|
+
*"repo-wide/deep compositions can take minutes … use deep jobs nightly"* —
|
|
540
|
+
which stayed true for as long as the anchors behind it did and then went on
|
|
541
|
+
being printed for two more releases, recommending a nightly job for commands
|
|
542
|
+
that had come to finish in seconds. A sentence maintained beside the table it
|
|
543
|
+
describes is the second copy of a fact, which is the rule this module opens
|
|
544
|
+
with. It is generated from the anchors instead, so refreshing them refreshes
|
|
545
|
+
the prose.
|
|
546
|
+
"""
|
|
547
|
+
audit = next((r for r in COMMANDS if r.command == "spring-audit"), None)
|
|
548
|
+
seconds = FIELD_ANCHOR_SECONDS if audit is None or audit.field_seconds is None else audit.field_seconds
|
|
549
|
+
return (
|
|
550
|
+
f"Cost tracks the command class and the build, not the file count: "
|
|
551
|
+
f"per-symbol queries are the fast path, inventory commands scale with "
|
|
552
|
+
f"files, and a repository-wide audit measured {seconds:g} s on a "
|
|
553
|
+
f"{_thousands(FIELD_ANCHOR_JAVA_FILES)}-file repository "
|
|
554
|
+
f"({FIELD_ANCHOR_MEASURED_VERSION}). `ask cache model` prints the figure "
|
|
555
|
+
f"and the build behind it for every command; "
|
|
556
|
+
f"ASK_MAX_ANALYSIS_SECONDS/ASK_PROGRESS bound and narrate a run where CI "
|
|
557
|
+
f"wants an explicit budget."
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def field_currency() -> dict:
|
|
562
|
+
"""The same statement as `reference_currency`, for the field anchors (C3-97).
|
|
563
|
+
|
|
564
|
+
Published so a consumer can see which side of the pair is current without
|
|
565
|
+
parsing prose, and so the gap is visible the day it opens rather than the day
|
|
566
|
+
an evaluation points at it.
|
|
567
|
+
"""
|
|
568
|
+
from sourcecode import __version__
|
|
569
|
+
|
|
570
|
+
current = __version__
|
|
571
|
+
measured = [
|
|
572
|
+
row for row in COMMANDS
|
|
573
|
+
if row.field_seconds is not None or row.field_blocked
|
|
574
|
+
]
|
|
575
|
+
stale = sorted(
|
|
576
|
+
row.command
|
|
577
|
+
for row in measured
|
|
578
|
+
if row.field_measured_version not in (None, current)
|
|
579
|
+
)
|
|
580
|
+
# C3-100: the other staleness, and the one that hid for three releases — not
|
|
581
|
+
# "older than the build you are running" (every row is, the day after a
|
|
582
|
+
# release) but "older than the newest measurement in this same table", which
|
|
583
|
+
# is a refresh that reached some rows and not others. Named here so the gap is
|
|
584
|
+
# readable without diffing the rows by hand.
|
|
585
|
+
newest = newest_field_measurement_version()
|
|
586
|
+
behind = sorted(
|
|
587
|
+
row.command
|
|
588
|
+
for row in measured
|
|
589
|
+
if _version_key(row.field_measured_version) < _version_key(newest)
|
|
590
|
+
)
|
|
591
|
+
lagging = (
|
|
592
|
+
f" {len(behind)} of the {len(measured)} field figures were taken before "
|
|
593
|
+
f"the newest one in this table ({newest}): "
|
|
594
|
+
f"{', '.join(behind)}."
|
|
595
|
+
if behind
|
|
596
|
+
else ""
|
|
597
|
+
)
|
|
598
|
+
return {
|
|
599
|
+
"anchor": FIELD_ANCHOR,
|
|
600
|
+
"measured_on_version": FIELD_ANCHOR_MEASURED_VERSION,
|
|
601
|
+
"running_version": current,
|
|
602
|
+
"current": FIELD_ANCHOR_MEASURED_VERSION == current,
|
|
603
|
+
"rows_measured_on_an_earlier_build": stale,
|
|
604
|
+
"newest_field_measurement_version": newest,
|
|
605
|
+
"rows_behind_the_newest_field_measurement": behind,
|
|
606
|
+
"statement": (
|
|
607
|
+
(
|
|
608
|
+
f"The field anchors were taken on {FIELD_ANCHOR_MEASURED_VERSION}; you "
|
|
609
|
+
f"are running {current}. Each row states the build its field figure "
|
|
610
|
+
f"belongs to, and no figure is projected onto this one."
|
|
611
|
+
if FIELD_ANCHOR_MEASURED_VERSION != current
|
|
612
|
+
else f"The field anchors were taken on {current}, the build you are running."
|
|
613
|
+
)
|
|
614
|
+
+ lagging
|
|
615
|
+
),
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
|
|
372
619
|
def reference_currency() -> dict:
|
|
373
620
|
"""How old the reference figures are, in releases the reader can name (F-AP).
|
|
374
621
|
|
|
@@ -429,9 +676,10 @@ def as_dict(here: "Optional[Conditioning]" = None) -> dict:
|
|
|
429
676
|
# table nine releases old cannot read as this build's measurement.
|
|
430
677
|
"reference_currency": reference_currency(),
|
|
431
678
|
# Published beside the reference so no consumer reads the figures as a
|
|
432
|
-
# law: the same command
|
|
433
|
-
#
|
|
679
|
+
# law: the same command on the same commit of the same repository has
|
|
680
|
+
# measured 2 351 s and 8,4 s on two of our own builds (C4-19, C3-97).
|
|
434
681
|
"field_anchor": FIELD_ANCHOR,
|
|
682
|
+
"field_currency": field_currency(),
|
|
435
683
|
"commands": [
|
|
436
684
|
{
|
|
437
685
|
"command": cmd.command,
|
|
@@ -453,6 +701,21 @@ def as_dict(here: "Optional[Conditioning]" = None) -> dict:
|
|
|
453
701
|
if cmd.cold_seconds is None
|
|
454
702
|
else {"cold": cmd.cold_seconds, "warm": cmd.warm_seconds}
|
|
455
703
|
),
|
|
704
|
+
# C3-97: the field figure and the build it belongs to travel
|
|
705
|
+
# together, or not at all. A consumer that reads the seconds
|
|
706
|
+
# without the build is the defect this key exists to prevent.
|
|
707
|
+
"field_measurement": (
|
|
708
|
+
None
|
|
709
|
+
if cmd.field_seconds is None and not cmd.field_blocked
|
|
710
|
+
else {
|
|
711
|
+
"seconds": cmd.field_seconds,
|
|
712
|
+
"did_not_finish": cmd.field_blocked,
|
|
713
|
+
"java_files": FIELD_ANCHOR_JAVA_FILES,
|
|
714
|
+
"cache_state": cmd.field_cache_state,
|
|
715
|
+
"measured_on_version": cmd.field_measured_version,
|
|
716
|
+
"note": field_anchor_note(cmd),
|
|
717
|
+
}
|
|
718
|
+
),
|
|
456
719
|
}
|
|
457
720
|
for cmd in COMMANDS
|
|
458
721
|
],
|
|
@@ -478,6 +741,13 @@ def render_markdown() -> str:
|
|
|
478
741
|
f"performance gate (`docs/perf/REGRESSION-GATE.md`) is what re-measures them."
|
|
479
742
|
)
|
|
480
743
|
out.append("")
|
|
744
|
+
out.append(
|
|
745
|
+
f"The second anchor is the field one, and it carries its own build for the "
|
|
746
|
+
f"same reason (C3-97): {FIELD_ANCHOR}. Where a command was measured on an "
|
|
747
|
+
f"earlier release than the one you are running, `ask cache model` says so "
|
|
748
|
+
f"on that row rather than presenting the figure as this build's."
|
|
749
|
+
)
|
|
750
|
+
out.append("")
|
|
481
751
|
out.append("| Command | A warm gives it | Measured (nothing cached → after a warm) | Repeat run cached | Layers | Notes |")
|
|
482
752
|
out.append("|---|---|---|---|---|---|")
|
|
483
753
|
for cmd in COMMANDS:
|
|
@@ -556,12 +826,28 @@ def render_text(here: "Optional[Conditioning]" = None) -> str:
|
|
|
556
826
|
lines.append("What a warm gives each command")
|
|
557
827
|
lines.append(f" Timings: {REFERENCE_REPOSITORY}, each command measured in isolation.")
|
|
558
828
|
lines.append(f" {reference_currency()['statement']}")
|
|
829
|
+
lines.append(f" {field_currency()['statement']}")
|
|
559
830
|
width = max(len(c.command) for c in COMMANDS)
|
|
560
831
|
for cmd in COMMANDS:
|
|
561
832
|
repeat = "repeat cached" if cmd.repeat else "recomputes"
|
|
562
833
|
lines.append(f" {cmd.command.ljust(width)} {_WARM_LABEL[cmd.warm]:<16} ({repeat})")
|
|
563
834
|
if cmd.measured:
|
|
564
835
|
lines.append(f" {' ' * width} measured: {cmd.measured}")
|
|
836
|
+
if cmd.field_seconds is not None or cmd.field_blocked:
|
|
837
|
+
# C3-97: never the seconds without the build they were taken on.
|
|
838
|
+
# C3-100: nor without the cache state they were taken in.
|
|
839
|
+
note = field_anchor_note(cmd)
|
|
840
|
+
shown = (
|
|
841
|
+
"did not finish in an interactive session"
|
|
842
|
+
if cmd.field_seconds is None
|
|
843
|
+
else f"{cmd.field_seconds:g} s"
|
|
844
|
+
)
|
|
845
|
+
qualifiers = [f"{cmd.field_cache_state} cache"] + ([note] if note else [])
|
|
846
|
+
suffix = f" ({', '.join(qualifiers)})"
|
|
847
|
+
lines.append(
|
|
848
|
+
f" {' ' * width} field: {shown} at "
|
|
849
|
+
f"{_thousands(FIELD_ANCHOR_JAVA_FILES)} Java files{suffix}"
|
|
850
|
+
)
|
|
565
851
|
if cmd.note:
|
|
566
852
|
lines.append(f" {' ' * width} {cmd.note}")
|
|
567
853
|
if here is not None and cmd.command in here.per_command:
|
sourcecode/cli.py
CHANGED
|
@@ -467,6 +467,25 @@ def _help_scope() -> "Optional[Any]":
|
|
|
467
467
|
return None
|
|
468
468
|
|
|
469
469
|
|
|
470
|
+
def _cost_sentence() -> str:
|
|
471
|
+
"""What the front page says about cost, from the module that measured it.
|
|
472
|
+
|
|
473
|
+
CL-21: this paragraph used to be written here, went stale with the anchors it
|
|
474
|
+
paraphrased, and kept telling operators to schedule a nightly job for commands
|
|
475
|
+
that finish in seconds. It is `cache_model`'s sentence now, so it cannot drift
|
|
476
|
+
from the table `ask cache model` prints.
|
|
477
|
+
"""
|
|
478
|
+
try:
|
|
479
|
+
from sourcecode.cache_model import cost_sentence
|
|
480
|
+
|
|
481
|
+
return cost_sentence()
|
|
482
|
+
except Exception:
|
|
483
|
+
return (
|
|
484
|
+
"Cost tracks the command class and the build: `ask cache model` prints "
|
|
485
|
+
"what each command measured, and on which build."
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
|
|
470
489
|
def _build_help_text(scope: "Optional[Any]" = None) -> str:
|
|
471
490
|
"""Build --help text dynamically based on current license state."""
|
|
472
491
|
try:
|
|
@@ -485,10 +504,7 @@ def _build_help_text(scope: "Optional[Any]" = None) -> str:
|
|
|
485
504
|
Deterministic Java/Spring semantics and reusable structural context for AI coding agents.
|
|
486
505
|
|
|
487
506
|
Cache warms on first scan; later calls reuse pre-built context instead of rescanning.
|
|
488
|
-
|
|
489
|
-
inventory commands scale with files, and repo-wide/deep compositions can take
|
|
490
|
-
minutes on multi-thousand-endpoint repositories. Use deep jobs nightly or with
|
|
491
|
-
ASK_MAX_ANALYSIS_SECONDS/ASK_PROGRESS when CI needs an explicit budget.
|
|
507
|
+
{_cost_sentence()}
|
|
492
508
|
|
|
493
509
|
{_start_here_block(scope)}
|
|
494
510
|
|
sourcecode/detach.py
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"""detach.py — run a repository-wide analysis so it outlives the shell that started it.
|
|
2
2
|
|
|
3
|
-
C3-53, three reproductions on the field repository: `spring-audit`
|
|
4
|
-
analysis
|
|
5
|
-
|
|
3
|
+
C3-53, three reproductions on the field repository: `spring-audit` ran 408 s of
|
|
4
|
+
analysis **on the build measured then** (4.7.0-class; the same command on the same
|
|
5
|
+
repository measures 8,4 s on 4.18.0 — C3-97), the agent harness promoted the
|
|
6
|
+
foreground process to background at ~600 s, and the session died. The cost that
|
|
7
|
+
provoked this module has since collapsed; what the module answers — *a run that
|
|
8
|
+
must outlive the shell that started it* — has not, because a nightly composition
|
|
9
|
+
or a `risk` on a large tree still crosses that boundary. **Backgrounding it did not help, and neither did
|
|
6
10
|
`Start-Process -NoNewWindow -PassThru`** — all six detached processes died at
|
|
7
11
|
the same second the session did.
|
|
8
12
|
|