sourcecode 5.0.1__py3-none-any.whl → 5.2.0__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 CHANGED
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "5.0.1"
7
+ __version__ = "5.2.0"
@@ -235,6 +235,63 @@ def diff_metrics(base: ArchMetrics, head: ArchMetrics) -> dict:
235
235
  }
236
236
 
237
237
 
238
+ #: The one sentence both identical-state payloads are derived from (C3-107).
239
+ #: It is an argument, not an assumption, and it rests on a property this product
240
+ #: publishes and asserts every release: the analysis is a pure function of the
241
+ #: tree state, byte-identical across eleven field rounds and two major jumps.
242
+ IDENTICAL_STATE_BASIS = (
243
+ "The two states are one tree, and the analysis is a deterministic function of "
244
+ "the tree state, so every delta is zero without reading the tree. What is "
245
+ "*not* claimed is anything about the tree itself: the per-side totals were "
246
+ "not measured and are `null`, never 0 — pass two different states, or run "
247
+ "`ask <repo>` on one of them, to measure the repository rather than the diff."
248
+ )
249
+
250
+
251
+ def identical_state_delta() -> dict:
252
+ """The `architectural-delta-v1` payload for two states that are one state.
253
+
254
+ C3-107 closed the second build; the eleventh round measured what was left —
255
+ `delta` 24,6 s and `contract-diff` 59,9 s to conclude that a tree does not
256
+ differ from itself, from a purged cache. Building one side to diff it against
257
+ itself is a measurement of the *repository*, and this command's answer is the
258
+ *difference*: with one tree state the difference is empty by construction.
259
+
260
+ So the deltas are published as the zeroes they provably are, and every figure
261
+ that would require an analysis is `null` with the reason attached — the rule
262
+ this product applies everywhere else (an axis that was not measured is
263
+ `unknown`, never `0`).
264
+ """
265
+ empty_counts = {"base": None, "head": None, "delta": 0}
266
+ return {
267
+ "schema": ARCH_DELTA_SCHEMA,
268
+ "base_cir_hash": None,
269
+ "head_cir_hash": None,
270
+ "totals": {
271
+ "files": dict(empty_counts),
272
+ "symbols": dict(empty_counts),
273
+ "endpoints": dict(empty_counts),
274
+ "dependency_edges": dict(empty_counts),
275
+ "import_cycles": dict(empty_counts),
276
+ },
277
+ "endpoint_surface": {
278
+ "added_count": 0, "removed_count": 0, "added": [], "removed": [],
279
+ },
280
+ "fan_in_shifts": {"changed_count": 0, "shifts": []},
281
+ "import_cycle_shifts": {
282
+ "added_count": 0, "removed_count": 0, "added": [], "removed": [],
283
+ },
284
+ "blast_radius_shifts": {
285
+ "scope": "fan_in_shift_symbols", "changed_count": 0, "shifts": [],
286
+ },
287
+ "analysis": {"performed": False, "basis": IDENTICAL_STATE_BASIS},
288
+ "provenance": (
289
+ "architectural_delta (D1), identical states: no repository was read. "
290
+ + IDENTICAL_STATE_BASIS
291
+ ),
292
+ }
293
+
294
+
238
295
  def _transitive_reach(fqn: str, reverse_graph: dict, node_cap: int) -> int:
239
296
  """Size of the transitive reverse-dependency set of `fqn` (its blast radius).
240
297
 
sourcecode/cache_model.py CHANGED
@@ -141,10 +141,11 @@ REFERENCE_JAVA_FILES = 2000
141
141
  REFERENCE_MEASURED_VERSION = "3.2.2"
142
142
 
143
143
  #: The other end of the measured range, and the reason this module publishes a
144
- #: *class* rather than a projected duration (C4-19). Field evaluation #22, on
145
- #: Windows 11 / PowerShell 5.1 / pipx / Python 3.10: `spring-audit` on a
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.
144
+ #: *class* rather than a projected duration (C4-19). Field evaluation #26, on
145
+ #: Windows 11 / PowerShell 5.1 / pipx / Python 3.10, every cache purged first:
146
+ #: `spring-audit` on a 3 342-file repository ran **31,0 s** — the same command
147
+ #: takes 8.8 s on the 2 000-file reference, for a repository 1,7× the size, and it
148
+ #: ran 8,4 s on the same subject two releases earlier.
148
149
  #:
149
150
  #: **The refusal to project stands, and this evaluation strengthened it** (C3-97).
150
151
  #: For eight releases the pair of anchors said *cost does not track file count*;
@@ -159,9 +160,11 @@ REFERENCE_MEASURED_VERSION = "3.2.2"
159
160
  #: each was measured on**, and how to run it.
160
161
  #:
161
162
  #: 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.
163
+ #: 408 s (#13) → 2 351 s (#16, C3-72) → 8,4 s (#22, C3-97) → 31,0 s (#26, C3-110).
164
+ #: The middle figure was quoted as an operational anchor for four releases after
165
+ #: the build that produced it, and told operators to detach commands that finish in
166
+ #: seconds; the last one moved 3,7× in two releases with byte-identical payloads,
167
+ #: which is the movement the release gate exists to catch and did not.
165
168
  #:
166
169
  #: ⚠ `FIELD_ANCHOR`, `FIELD_ANCHOR_SECONDS` and `FIELD_ANCHOR_MEASURED_VERSION`
167
170
  #: are **derived from the `spring-audit` row**, below `COMMANDS`. They used to be
@@ -242,12 +245,12 @@ COMMANDS: tuple[CommandCache, ...] = (
242
245
  "71,7 s again for `--agent` on the state it had just analysed (C3-103).",
243
246
  "--compact 13.3 s cold → 0.3 s warm (cold re-measured on 3.7.0: was 19.3 s, C3-6); "
244
247
  "--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"),
248
+ field_seconds=166.7, field_cache_state="cold", field_measured_version="5.1.0"),
246
249
  CommandCache("posture", ("cir", "parse"), "shared", False,
247
250
  "Resolves the conditional bean graph on every run, over the shared CIR a warm "
248
251
  "builds — the parse it used to repeat for itself. `--diff` compares two profile "
249
252
  "sets over that one IR, so the second side costs the resolution only.",
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"),
253
+ "10.1 s → 1.6 s", analysis_class="repo-wide", cold_seconds=10.1, warm_seconds=1.6, field_seconds=8.3, field_cache_state="mixed", field_measured_version="5.1.0"),
251
254
  CommandCache("risk", ("cir", "parse"), "shared", False,
252
255
  "Composes what the audit, impact-chain and the posture already answer, so it "
253
256
  "pays each of their costs once over the shared CIR a warm builds — one parse "
@@ -255,7 +258,7 @@ COMMANDS: tuple[CommandCache, ...] = (
255
258
  "within the run.",
256
259
  "not measured on the battery yet — the composition is bounded by the "
257
260
  "`spring-audit` + `impact-chain` costs listed here, not by new analysis",
258
- analysis_class="deep", field_seconds=73.3, field_measured_version="4.18.0"),
261
+ analysis_class="deep", field_seconds=77.2, field_cache_state="mixed", field_measured_version="5.1.0"),
259
262
  CommandCache("enrich", ("cir", "parse"), "shared", False,
260
263
  "Runs the same composition as `risk` over the repository, then joins a SARIF "
261
264
  "log to it. Reading the log is negligible; everything a warm helps with is the "
@@ -280,30 +283,31 @@ COMMANDS: tuple[CommandCache, ...] = (
280
283
  "builds. Cost scales with the number of declared types, not with the "
281
284
  "size of the label.",
282
285
  "not measured on the battery yet — one `impact-chain` traversal per "
283
- "declared seed type over a CIR the warm already paid for", analysis_class="repo-wide"),
286
+ "declared seed type over a CIR the warm already paid for", analysis_class="repo-wide",
287
+ field_seconds=28.9, field_cache_state="mixed", field_measured_version="5.1.0"),
284
288
  CommandCache("endpoints", ("ris", "parse"), "shared", False,
285
289
  "Recomputes the endpoint surface on every run, over a parse a warm has already "
286
290
  "paid for. Until 3.7.0 the extractor parsed every file itself instead of reading "
287
291
  "the shared parse cache, and a warm measurably bought it nothing (C3-6).",
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"),
292
+ "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=5.6, field_cache_state="mixed", field_measured_version="5.1.0"),
289
293
  CommandCache("spring-audit", ("ris", "parse"), "shared", False,
290
294
  "Recomputes every run, but over a parse a warm has already paid for.",
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"),
295
+ "8.8 s → 3.7 s", analysis_class="repo-wide", cold_seconds=8.8, warm_seconds=3.7, field_seconds=31.0, field_cache_state="mixed", field_measured_version="5.1.0"),
292
296
  CommandCache("migrate-check", ("cir",), "none", False,
293
297
  "Computes its own inventory and shares nothing a warm builds. Only `--blast-radius` "
294
298
  "reuses the shared CIR.",
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"),
299
+ "4.8 s → 4.8 s", analysis_class="repo-wide", cold_seconds=4.8, warm_seconds=4.8, field_seconds=11.3, field_cache_state="mixed", field_measured_version="5.1.0"),
296
300
  CommandCache("impact-chain", ("cir", "parse"), "shared", False,
297
301
  "The CIR is the expensive half — this is where a warm pays most.",
298
302
  "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"),
303
+ field_seconds=16.1, field_cache_state="mixed", field_measured_version="5.1.0"),
300
304
  CommandCache("impact", ("parse",), "shared", False, "", "4.9 s → 2.8 s", analysis_class="core", cold_seconds=4.9, warm_seconds=2.8,
301
305
  field_seconds=7.7, field_measured_version="4.18.0"),
302
306
  CommandCache("pr-impact", ("parse",), "shared", False,
303
307
  "Diff-dependent: the answer itself is never stored. What it costs follows the "
304
308
  "diff, not the repository: 12,5 s on an ordinary one and 11,8 s on a diff of "
305
309
  "security configuration, measured at field scale.", analysis_class="core",
306
- field_seconds=12.5, field_measured_version="4.18.0"),
310
+ field_seconds=25.7, field_cache_state="mixed", field_measured_version="5.1.0"),
307
311
  CommandCache("verify", (), "none", False, "Runs the contracts against a fresh reading.", analysis_class="core"),
308
312
  CommandCache("verify-edit", ("parse",), "shared", True,
309
313
  "Built for the edit loop: the parse cache is what keeps an unchanged file out of the "
@@ -311,15 +315,17 @@ COMMANDS: tuple[CommandCache, ...] = (
311
315
  "yet: eval #23 measured 76,6 s on a tree with no edits, because the HEAD side is "
312
316
  "still built in a throwaway worktree instead of reusing the shared CIR (C3-102).",
313
317
  "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"),
318
+ field_seconds=119.5, field_cache_state="mixed", field_measured_version="5.1.0"),
315
319
  CommandCache("review-pr", ("cir",), "none", False,
316
320
  "Diff-dependent, and it reuses the CIR only if one exists. On a small diff, loading "
317
321
  "the warmed CIR costs more than the work it saves.",
318
322
  "1.1 s → 2.3 s (slower)", analysis_class="core", cold_seconds=1.1, warm_seconds=2.3),
319
323
  CommandCache("plan", ("parse",), "shared", False, "", "9.3 s → 3.8 s", analysis_class="core", cold_seconds=9.3, warm_seconds=3.8),
320
324
  CommandCache("compare", ("parse",), "shared", False, "", analysis_class="core"),
321
- CommandCache("delta", (), "none", False, "Analyses two states — two checkouts, or two refs materialised into temporary trees; neither is the tree the cache describes.", analysis_class="repo-wide"),
322
- CommandCache("contract-diff", (), "none", False, "Analyses two states (checkouts or refs); neither is the tree the cache describes.", analysis_class="repo-wide"),
325
+ CommandCache("delta", (), "none", False, "Analyses two states — two checkouts, or two refs materialised into temporary trees; neither is the tree the cache describes.", analysis_class="repo-wide",
326
+ field_seconds=24.6, field_cache_state="mixed", field_measured_version="5.1.0"),
327
+ CommandCache("contract-diff", (), "none", False, "Analyses two states (checkouts or refs); neither is the tree the cache describes.", analysis_class="repo-wide",
328
+ field_seconds=59.9, field_cache_state="mixed", field_measured_version="5.1.0"),
323
329
  CommandCache("fix-bug", ("task",), "none", True,
324
330
  "Shorthand for `prepare-context fix-bug`; caches its own answer, which a warm never runs.", analysis_class="core"),
325
331
  CommandCache("rename-class", (), "none", False, "", analysis_class="core"),
@@ -341,26 +347,27 @@ COMMANDS: tuple[CommandCache, ...] = (
341
347
  "no answer hit", analysis_class="repo-wide", cold_seconds=6.5, warm_seconds=0.3),
342
348
  CommandCache("explain", ("cir",), "shared", False, "Serves from the shared CIR a warm builds.",
343
349
  "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"),
350
+ field_seconds=6.3, field_cache_state="mixed", field_measured_version="5.1.0"),
345
351
  CommandCache("export", ("parse",), "shared", False, "", "8.8 s → 3.7 s", analysis_class="repo-wide", cold_seconds=8.8, warm_seconds=3.7),
346
352
  CommandCache("repo-ir", ("parse",), "shared", False,
347
353
  "Carried as *did not finish* from 4.10.4 until eval #23 ran it at field size in "
348
354
  "9,8 s — a run that did not finish once is not a command that cannot finish "
349
355
  "(C3-100, and the same correction `modernize` needed).",
350
356
  "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"),
357
+ field_seconds=19.2, field_cache_state="mixed", field_measured_version="5.1.0"),
352
358
  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"),
359
+ field_seconds=39.5, field_cache_state="mixed", field_measured_version="5.1.0"),
354
360
  CommandCache("modernize", ("parse",), "shared", False,
355
361
  "Blocked in the session that recorded C3-53 and measured since: a run that did "
356
362
  "not finish once is not a command that cannot finish.",
357
363
  "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"),
364
+ field_seconds=17.1, field_cache_state="mixed", field_measured_version="5.1.0"),
359
365
  CommandCache("chunk-file", (), "none", False, "Reads one file; nothing to cache.", analysis_class="core"),
360
366
  CommandCache("cold-start", ("ris",), "answer", True,
361
367
  "Reads the RIS a warm rebuilds — that is all it does. Without one it answers "
362
368
  "`no_ris` instead of a snapshot.",
363
- "0.2 s either way", analysis_class="core", cold_seconds=0.2, warm_seconds=0.2),
369
+ "0.2 s either way", analysis_class="core", cold_seconds=0.2, warm_seconds=0.2,
370
+ field_seconds=0.4, field_cache_state="mixed", field_measured_version="5.1.0"),
364
371
  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"),
365
372
  CommandCache("baseline", ("parse",), "shared", False,
366
373
  "`capture`/`diff`/`trend` over architectural metrics. The field figure is `capture`, "
@@ -405,13 +412,15 @@ def _row(command: str) -> "CommandCache":
405
412
 
406
413
  #: The headline anchor, read off the table rather than written twice (C3-100).
407
414
  #: `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.
415
+ #: (#16), 8,4 s (#22), 31,0 s (#26) — so it is the row the front page quotes, and
416
+ #: quoting it means reading it, not copying it. The figure moving *up* between two
417
+ #: rounds with byte-identical payloads is the reason C3-110 exists: nothing in this
418
+ #: repository measured the direction, and the anchor is the only place it shows.
410
419
  _FIELD_ANCHOR_ROW = _row("spring-audit")
411
420
  FIELD_ANCHOR_SECONDS = _FIELD_ANCHOR_ROW.field_seconds
412
421
  FIELD_ANCHOR_MEASURED_VERSION = _FIELD_ANCHOR_ROW.field_measured_version
413
422
  FIELD_ANCHOR = (
414
- f"field evaluation #22: spring-audit on {_thousands(FIELD_ANCHOR_JAVA_FILES)} "
423
+ f"field evaluation #26: spring-audit on {_thousands(FIELD_ANCHOR_JAVA_FILES)} "
415
424
  f"Java files took {FIELD_ANCHOR_SECONDS:g} s (Windows, pipx, "
416
425
  f"{_FIELD_ANCHOR_ROW.field_cache_state} cache) against "
417
426
  f"{_FIELD_ANCHOR_ROW.cold_seconds:g} s on the "
@@ -532,6 +541,37 @@ def field_anchor_note(row: "Optional[CommandCache]") -> "Optional[str]":
532
541
  return f"on {measured_on}"
533
542
 
534
543
 
544
+ def field_anchor_currency_clause(row: "Optional[CommandCache]") -> "Optional[str]":
545
+ """The same fact as :func:`field_anchor_note`, said where it is *acted on* (C3-110).
546
+
547
+ C3-109 made every field figure carry its build, and the eleventh round confirms
548
+ the label is read and believed — and then reports the gap it does not close:
549
+ `spring-audit` publishes *"8,4 s … (4.18.0)"* while the same command measures
550
+ 31,0 s on the build the reader is running. `(on 4.18.0)` is the right label for
551
+ a **row in a table**, where the reader is comparing figures; it is not enough
552
+ on a line that tells somebody how to run something, because there the figure is
553
+ being used as advice about the build in hand.
554
+
555
+ So the same fact gets a longer form for the surfaces that advise: the build,
556
+ and the admission that nothing has re-measured it here. That admission is the
557
+ registry this product applies everywhere else (`counts_are_floor`,
558
+ `unchanged_for`, `confidence_basis`) and the one place it had not applied to
559
+ itself.
560
+
561
+ ``None`` when the figure was taken on the running build — there is nothing to
562
+ admit — or when there is no figure at all.
563
+ """
564
+ if row is None or (row.field_seconds is None and not row.field_blocked):
565
+ return None
566
+ from sourcecode import __version__
567
+
568
+ measured_on = row.field_measured_version
569
+ if measured_on == __version__:
570
+ return None
571
+ where = f"measured on {measured_on}" if measured_on else "build not recorded"
572
+ return f"{where}; this build has not been re-measured at that scale"
573
+
574
+
535
575
  def cost_sentence() -> str:
536
576
  """The one paragraph the front page says about cost, derived here (CL-21).
537
577
 
@@ -544,15 +584,38 @@ def cost_sentence() -> str:
544
584
  with. It is generated from the anchors instead, so refreshing them refreshes
545
585
  the prose.
546
586
  """
587
+ from sourcecode import __version__
588
+
547
589
  audit = next((r for r in COMMANDS if r.command == "spring-audit"), None)
548
590
  seconds = FIELD_ANCHOR_SECONDS if audit is None or audit.field_seconds is None else audit.field_seconds
591
+ # C3-110: the figure is history the moment it is published, and this sentence
592
+ # is the surface a reader budgets against. The field measured 31,0 s where this
593
+ # line said 8,4 s, and the line was not wrong — it named 4.18.0 — it just left
594
+ # the reader to notice that 4.18.0 is not the build they are running. Said
595
+ # outright, and paired with the one figure this project *can* refresh: the gate
596
+ # measurement of the build in hand, when there is one.
597
+ stale = (
598
+ ""
599
+ if FIELD_ANCHOR_MEASURED_VERSION == __version__
600
+ else f", and {__version__} has not been re-measured at that scale"
601
+ )
602
+ gate = ""
603
+ _gate_cell = gate_anchor("spring-audit")
604
+ _gate_state = gate_currency()
605
+ if _gate_cell and _gate_state.get("current"):
606
+ gate = (
607
+ f" On this build the release gate measured the same command at "
608
+ f"{_gate_cell['seconds']:g} s on a "
609
+ f"{_thousands(int(_gate_cell['java_files']))}-file repository "
610
+ f"({_gate_cell['cache_state']} cache, {_gate_cell['host_class']})."
611
+ )
549
612
  return (
550
613
  f"Cost tracks the command class and the build, not the file count: "
551
614
  f"per-symbol queries are the fast path, inventory commands scale with "
552
615
  f"files, and a repository-wide audit measured {seconds:g} s on a "
553
616
  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; "
617
+ f"({FIELD_ANCHOR_MEASURED_VERSION}{stale}).{gate} `ask cache model` prints "
618
+ f"the figure and the build behind it for every command; "
556
619
  f"ASK_MAX_ANALYSIS_SECONDS/ASK_PROGRESS bound and narrate a run where CI "
557
620
  f"wants an explicit budget."
558
621
  )
@@ -616,6 +679,103 @@ def field_currency() -> dict:
616
679
  }
617
680
 
618
681
 
682
+ def gate_anchor(command: str) -> "Optional[dict]":
683
+ """What the release gate measured for *command*, or ``None`` (C3-109).
684
+
685
+ The third figure in this module, and the only one this repository can refresh
686
+ by itself. `REFERENCE_*` is a 3.2.2 measurement on a 2 000-file tree;
687
+ `field_seconds` is the field's, at 3 342 files, on their machine and their
688
+ build. Both age, and neither can be re-measured from here — which is exactly
689
+ how B17 reopened one release after it was closed: the labelling worked and
690
+ the number still expired.
691
+
692
+ The gate measures a 2 985-file repository under a declared host class every
693
+ release and commits its cells, so this figure carries the build that produced
694
+ it and can be brought current by running the gate. It never overrides a field
695
+ anchor — a measurement at field scale outranks one at gate scale — it is
696
+ published *beside* it, so an anchor from an older build is no longer the only
697
+ number a reader has.
698
+ """
699
+ try:
700
+ from sourcecode.gate_anchors import (
701
+ GATE_ANCHORS,
702
+ GATE_HOST_CLASS,
703
+ GATE_JAVA_FILES,
704
+ GATE_MEASURED_VERSION,
705
+ GATE_REPOSITORY,
706
+ )
707
+ except Exception:
708
+ return None
709
+ cell = GATE_ANCHORS.get(command)
710
+ if not cell:
711
+ return None
712
+ return {
713
+ "seconds": cell["seconds"],
714
+ "cache_state": cell["mode"],
715
+ "runs": cell["runs"],
716
+ "measured_on_version": GATE_MEASURED_VERSION,
717
+ "repository": GATE_REPOSITORY,
718
+ "java_files": GATE_JAVA_FILES,
719
+ "host_class": GATE_HOST_CLASS,
720
+ }
721
+
722
+
723
+ def gate_currency() -> dict:
724
+ """Whether the in-house gate figures describe the build in hand (C3-109).
725
+
726
+ Same shape and same purpose as `field_currency`, for the anchor set that can
727
+ actually be refreshed. When this says `current: false`, the remedy is a
728
+ command rather than a hope: re-run the gate on the tag and commit the cells.
729
+ """
730
+ from sourcecode import __version__
731
+
732
+ current = __version__
733
+ try:
734
+ from sourcecode.gate_anchors import (
735
+ GATE_ANCHORS,
736
+ GATE_CAPTURED_AT,
737
+ GATE_HOST_CLASS,
738
+ GATE_JAVA_FILES,
739
+ GATE_MEASURED_VERSION,
740
+ GATE_REPOSITORY,
741
+ )
742
+ except Exception:
743
+ return {
744
+ "available": False,
745
+ "running_version": current,
746
+ "statement": (
747
+ "This build carries no gate measurement of its own. The gate "
748
+ "publishes one per release (docs/perf/REGRESSION-GATE.md); until "
749
+ "it runs, the field anchors are the only measured figures here."
750
+ ),
751
+ }
752
+ return {
753
+ "available": True,
754
+ "measured_on_version": GATE_MEASURED_VERSION,
755
+ "running_version": current,
756
+ "current": GATE_MEASURED_VERSION == current,
757
+ "captured_at": GATE_CAPTURED_AT,
758
+ "repository": GATE_REPOSITORY,
759
+ "java_files": GATE_JAVA_FILES,
760
+ "host_class": GATE_HOST_CLASS,
761
+ "commands": sorted(GATE_ANCHORS),
762
+ "statement": (
763
+ f"The release gate measured {len(GATE_ANCHORS)} commands on "
764
+ f"{GATE_REPOSITORY} ({GATE_JAVA_FILES} Java files, {GATE_HOST_CLASS}) "
765
+ + (
766
+ f"on {current}, the build you are running."
767
+ if GATE_MEASURED_VERSION == current
768
+ else (
769
+ f"on {GATE_MEASURED_VERSION}; you are running {current}, which "
770
+ f"the gate has not measured. Re-run it on this build "
771
+ f"(docs/perf/REGRESSION-GATE.md) — unlike the field anchors, "
772
+ f"this is a figure this project can refresh."
773
+ )
774
+ )
775
+ ),
776
+ }
777
+
778
+
619
779
  def reference_currency() -> dict:
620
780
  """How old the reference figures are, in releases the reader can name (F-AP).
621
781
 
@@ -680,6 +840,11 @@ def as_dict(here: "Optional[Conditioning]" = None) -> dict:
680
840
  # measured 2 351 s and 8,4 s on two of our own builds (C4-19, C3-97).
681
841
  "field_anchor": FIELD_ANCHOR,
682
842
  "field_currency": field_currency(),
843
+ # C3-109: and the figure this project can refresh by itself. A field
844
+ # anchor belongs to the build the field ran; this one belongs to the
845
+ # build the gate ran, which is a release away at worst instead of
846
+ # unreachable. Published beside, never instead.
847
+ "gate_currency": gate_currency(),
683
848
  "commands": [
684
849
  {
685
850
  "command": cmd.command,
@@ -716,6 +881,12 @@ def as_dict(here: "Optional[Conditioning]" = None) -> dict:
716
881
  "note": field_anchor_note(cmd),
717
882
  }
718
883
  ),
884
+ # C3-109: the in-house measurement of this command, on the build
885
+ # the gate last ran. Absent for commands the gate does not cover —
886
+ # its population is declared in `sourcecode.perf`, with the
887
+ # exclusions and their reasons, and an axis nobody measured is
888
+ # never a number here.
889
+ "gate_measurement": gate_anchor(cmd.command),
719
890
  }
720
891
  for cmd in COMMANDS
721
892
  ],
@@ -827,6 +998,7 @@ def render_text(here: "Optional[Conditioning]" = None) -> str:
827
998
  lines.append(f" Timings: {REFERENCE_REPOSITORY}, each command measured in isolation.")
828
999
  lines.append(f" {reference_currency()['statement']}")
829
1000
  lines.append(f" {field_currency()['statement']}")
1001
+ lines.append(f" {gate_currency()['statement']}")
830
1002
  width = max(len(c.command) for c in COMMANDS)
831
1003
  for cmd in COMMANDS:
832
1004
  repeat = "repeat cached" if cmd.repeat else "recomputes"
@@ -848,6 +1020,17 @@ def render_text(here: "Optional[Conditioning]" = None) -> str:
848
1020
  f" {' ' * width} field: {shown} at "
849
1021
  f"{_thousands(FIELD_ANCHOR_JAVA_FILES)} Java files{suffix}"
850
1022
  )
1023
+ _gate = gate_anchor(cmd.command)
1024
+ if _gate:
1025
+ # C3-109: the in-house figure, beside the field's rather than instead
1026
+ # of it — one is at field scale on another build, one is on a build
1027
+ # this project can bring current.
1028
+ lines.append(
1029
+ f" {' ' * width} gate: {_gate['seconds']:g} s at "
1030
+ f"{_thousands(_gate['java_files'])} Java files "
1031
+ f"({_gate['cache_state']} cache, on {_gate['measured_on_version']}, "
1032
+ f"{_gate['host_class']})"
1033
+ )
851
1034
  if cmd.note:
852
1035
  lines.append(f" {' ' * width} {cmd.note}")
853
1036
  if here is not None and cmd.command in here.per_command: