sourcecode 5.8.7__py3-none-any.whl → 5.8.8__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/_docs/DEFECT-LEDGER.md +53 -2
- sourcecode/_docs/USER_GUIDE.md +1 -1
- sourcecode/cli.py +100 -12
- sourcecode/data_exposure.py +16 -4
- sourcecode/manifest_cache.py +1 -0
- sourcecode/path_filters.py +1 -1
- sourcecode/phased_run.py +9 -0
- sourcecode/prepare_context.py +8 -3
- sourcecode/release_info.py +1 -1
- sourcecode/repository_ir.py +2 -1
- sourcecode/selftest.py +17 -2
- sourcecode/summarizer.py +21 -4
- sourcecode/validation_surface.py +9 -1
- sourcecode/verify_edit.py +6 -1
- {sourcecode-5.8.7.dist-info → sourcecode-5.8.8.dist-info}/METADATA +3 -3
- {sourcecode-5.8.7.dist-info → sourcecode-5.8.8.dist-info}/RECORD +21 -21
- {sourcecode-5.8.7.dist-info → sourcecode-5.8.8.dist-info}/WHEEL +0 -0
- {sourcecode-5.8.7.dist-info → sourcecode-5.8.8.dist-info}/entry_points.txt +0 -0
- {sourcecode-5.8.7.dist-info → sourcecode-5.8.8.dist-info}/licenses/LICENSE +0 -0
- {sourcecode-5.8.7.dist-info → sourcecode-5.8.8.dist-info}/licenses/NOTICE +0 -0
sourcecode/__init__.py
CHANGED
|
@@ -14,6 +14,46 @@ 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
|
|
18
|
+
|
|
19
|
+
**As of 2026-08-19, HEAD `f39432b`:** the latest audit queue was implemented in fourteen
|
|
20
|
+
post-5.8.7 commits. These are committed follow-up fixes, not a tagged release. This table is
|
|
21
|
+
the authoritative status for round 11; detailed rows retain their original findings and
|
|
22
|
+
acceptance criteria for auditability.
|
|
23
|
+
|
|
24
|
+
| Rows | Current status | Evidence |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `B23` | **closed** | `e972396`; nested error-hint regression |
|
|
27
|
+
| `B24` | **closed** | `b62d840`; published budget scope |
|
|
28
|
+
| `B27`, `C4-25` residual, `C4-27` | **closed** | `f66ed7a`; aliases and bounded fuzzy suggestions |
|
|
29
|
+
| `B25` | **closed** | `fedfa65`; declaration-state disclosure |
|
|
30
|
+
| `C4-28` | **closed** | `04b9a10`; output destination confirmation |
|
|
31
|
+
| `B26` | **closed** | `883b370`; supported-extension wording |
|
|
32
|
+
| `B28` | **closed** | `ffd0a8f`; build-based Windows 11 label |
|
|
33
|
+
| `C1-49` | **closed for additive compatibility** | `a92a745`; new explicit field, legacy alias retained under `BC-002` |
|
|
34
|
+
| `C3-130` | **closed for `plan`/`cold-start`** | `c2a2ebf`; temporary spill with a recoverable path |
|
|
35
|
+
| `B4` | **closed** | `02a981f`; shared tool-state exclusions plus P1-proc evidence (`verify-edit` `1.049x`, `impact` control `1.081x`) |
|
|
36
|
+
| `C3-130` / cache invalidation | **closed** | `af79bb3`; dependency-cache keys now include `.csproj` and `packages.lock.json`, preventing stale cross-tree .NET results |
|
|
37
|
+
| `BUG-004` | **closed** | `f29312e`; Java discovery now applies the shared module-path classifier to `-test`/`-tests` modules |
|
|
38
|
+
| `C3-55` | **closed** | `63249df`; below-floor analysis budgets warn in noninteractive runs while execution continues |
|
|
39
|
+
| `C3-55` regression | **closed** | `f39432b`; above-floor provenance remains quiet off-TTY and is emitted interactively |
|
|
40
|
+
|
|
41
|
+
The current full-suite exceptions are environmental or pre-existing measurement gaps,
|
|
42
|
+
not silently accepted fixes: RIS tests cannot write the default `~/.sourcecode/cache`
|
|
43
|
+
under the managed sandbox; `cold-start` therefore reports `no_ris`. The E-6 module-name
|
|
44
|
+
publication fixture and the ASK-09 real-scale fixture still need a writable cache and
|
|
45
|
+
their original fixture assumptions before they can be classified conclusively.
|
|
46
|
+
|
|
47
|
+
The seven full-suite failures observed after these commits are tracked separately from this
|
|
48
|
+
queue: polyglot dependency parsing, Java test-module exclusion, RIS/cold-start fixture setup,
|
|
49
|
+
and the advisory-budget warning fixture. They are not silently counted as regressions or as
|
|
50
|
+
closed defects.
|
|
51
|
+
|
|
52
|
+
**Reading rule for the historical rows below:** their status text records the state at the
|
|
53
|
+
time each audit entry was written. When a historical row says `open` but appears in the table
|
|
54
|
+
above, the current status is the one in **Current Synchronization**; the original wording is
|
|
55
|
+
retained only as evidence of what the auditor observed before the fix.
|
|
56
|
+
|
|
17
57
|
---
|
|
18
58
|
|
|
19
59
|
## Audit synchronization
|
|
@@ -40,7 +80,7 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
|
|
|
40
80
|
| AS-18 | **The parse store's budget was published as a figure to size a disk by, and it is a floor of the guard, not a ceiling of the store.** The resting excess went 5,45 MB → 44,35 MB over the 512 MB budget and, followed across three further commands, **did not converge** (556,35 MB, steady). Nothing was lying — the policy is *"swept every 32 MB written"*, and with no writes there is no sweep — but whoever reserves disk on the published figure comes up 9 % short. | 5.8.0 re-audit (`ASK-15` in the roadmap; the auditor's own first reading, 96,18 MB, was retracted as transient and the row reclassified to low) | Low — a sizing defect, not an honesty one, which is exactly why the fix is disclosure | **closed 5.8.1** — `cache status` publishes `effective_ceiling_mb` beside the budget: `budget + sweep_interval × concurrent writers`, with the writer count read from the authority the parse pool resolves `--jobs` against, so the ceiling moves with the machine instead of restating a number. The basis names both terms and the reason the excess *rests*: the last interval before a process exits is never swept. `over_budget` (normal, bounded, expected) is now separated from **`over_ceiling`**, the only state that contradicts what we publish, and each carries its own note. `cache model` and `docs/CACHE.md` corrected from the same fact — the doc block is generated, so they cannot drift. Regression `tests/test_parse_store_effective_ceiling_ask15.py`, 8 assertions, including that a store with no budget publishes **no** ceiling rather than inventing one. |
|
|
41
81
|
| B15 | **A write that could not finish left its temporary inside the caller's repository.** The payload goes to `.<name>.tmp-<pid>-<ns>` beside the destination and is renamed onto it — atomic, and correct — but when the rename failed nothing removed the temporary. Field matrix of five bad destinations on Windows: **13,9 MB of untracked files inside the repository** (2 757 334 B per attempt plus a 153 481 B `.partial`), as `??` in `git status`. Three edges came with it: a discard sink (`nul`, `NUL`, `CON`, `/dev/null`) was reported as an I/O error for successfully throwing output away; the OS `strerror` was interpolated into our English message, so the same matrix produced two `es-ES` messages and one English; and `-o ./missing/out.json` created the tree in silence while the hint told the caller to check that the directory exists. | 5.6.1 audit, re-reported and widened in the 5.7.2 audits | **High** — it is what breaks the read-only premise the product is sold on, and the auditor grades it blocking for any pre-CI security review | **closed 5.8.1** — one authority, `sourcecode.output_sink`. The temporary is removed in a **`finally`**, so the guarantee does not depend on enumerating the exit paths correctly (which is how five escape routes were missed). A discard sink answers `written: false`, `sink: "null-device"`, `exit 0`, is matched only on the platform that reserves the name (a POSIX file called `nul` is a file), and a run writing there keeps its checkpoint in the partials directory instead of `nul.partial`. The platform's message travels in `os_error: {raw, errno, errno_name, locale}`, never inside the contract sentence, and the error publishes `cleaned_up: [...]` — what the run took back with it, so "read-only" is checkable rather than trusted. Parent directories are created, deliberately, and the hint says so. `phased_run` writes through the same authority and registers its checkpoint as this run's, so a failed answer removes it. Regression `tests/test_output_sink_leaves_nothing_b15.py`, 12 assertions, including the end-to-end matrix leaving the tree byte-identical and a negative control that cleanup can only ever remove what this run wrote. |
|
|
42
82
|
| ASK-10 | **A command with an output ceiling and no flag that can get under it.** `posture spring-boot` returned 355 032 B / `exit 0` in 5.6.1; the unified ceiling made it `exit 1` — the right call — and left no in-band way out: not `--limit`, not `--top-n`, not `--min-severity`, not `--compact`. The only routes were `--output FILE` (the answer leaves the reader's context) and `ASK_MAX_OUTPUT_TOKENS=0` (the guard off). `exit 1` on **2 of 8** corpus repositories, and `thingsboard` returned `exit 0` in 5.7.2 — so the trap closes as the product improves: the growth was **better detection**, `unresolved` 64 120 → 184 864 B because conditional beans now name `@ConditionalOnExpression` instead of a generic reason. `validation` inherits it. | 5.7.2 audits, escalated to high and blocking by the 5.8.0 re-audit | **High** — structural rather than incidental: every future gain in exhaustiveness pushes another command at the ceiling | **closed 5.8.1** — `posture` and `validation` declare `--limit N` and `--compact`, both already in the ceiling's vocabulary, so the hint offers them without being taught (verified: with the ceiling forced, both now answer *"Use --compact …, --limit N …"*). Bounding is one authority, `sourcecode.output_bounds`: it walks the payload so a `--diff` answer is bounded on **both** sides, touches lists and never counts (`summary.unresolved` is an int under the same name), and every cut publishes the `*_cap` contract — `{total, shown, omitted}` plus the registered effect — because a flag that cuts without declaring the cut is B14 again. **Nothing is bounded by default**: a payload that shrinks silently between releases is what AS-16 was filed for. Measured: `posture keycloak-config-cli` 53 115 → 4 605 B under `--compact`, summary byte-identical. The invariant is now derived from the CLI registry (`tests/test_bounding_flag_registry_ask10.py`): a command that can exceed the ceiling and declares no bounding flag fails the battery until somebody decides, with `cli.COMMANDS_WITHOUT_BOUNDING` carrying the exemptions and their reasons — including, on the record, the repository-scale commands that have not yet been measured against the ceiling. Behaviour battery `tests/test_output_bounds_ask10.py`, 13 assertions. |
|
|
43
|
-
| B4 | **`verify-edit` reported two repository-wide builds beside two empty lists, and nothing in the payload could explain them.** Reported as the empty-diff short circuit failing: clean worktree, `changed_files: []`, `changed_build_files: []`, `head_model_cache_hit: true`, `model_built: true`, 23 468 ms with `ASK_PROGRESS` never leaving *diffing working tree vs HEAD*. | 5.6.1 audit, re-reported through the 5.7.2 and 5.8.0 audits | High | **closed 5.8.1 — and the reported cause is not the cause.** Measured on BroadleafCommerce (2 985 `.java`) with a clean tree: **0,47 s, `model_built: false`** — the short circuit (C3-32 / C3-102 / AS-13) fires, and the row's stated acceptance (`< 2 000 ms`, `model_built: false`, `verdict: pass`) already held. The reported shape reproduces exactly with **one untracked `zz.yml`**: both lists empty, `model_built: true`, **16,3 s**. `_can_move_a_verdict` admits any suffix an axis may read — deliberately, since C3-14 — so a `.yml` that is not `application*` is neither a changed `.java` nor a changed build file, and appeared in **no** published list. The change set was never empty; the payload could not say so. `analysis.model_forced_by: {count, paths, reason}` is that sentence, and `basis` no longer reads *"0 changed .java file(s) and 0 changed build file(s)"* beside two full builds. Regression `tests/test_verify_edit_names_what_forced_the_model_b4.py`, 5 assertions, including the negative control that a changed `.java` file is not explained twice. ⚠ Still open and now separable: the two-build cost itself when a forcing path exists (the parse cache serves the parse; the IR assembly is paid twice), tracked as the profiling half. |
|
|
83
|
+
| B4 | **`verify-edit` reported two repository-wide builds beside two empty lists, and nothing in the payload could explain them.** Reported as the empty-diff short circuit failing: clean worktree, `changed_files: []`, `changed_build_files: []`, `head_model_cache_hit: true`, `model_built: true`, 23 468 ms with `ASK_PROGRESS` never leaving *diffing working tree vs HEAD*. | 5.6.1 audit, re-reported through the 5.7.2 and 5.8.0 audits | High | **closed 5.8.1 — and the reported cause is not the cause.** Measured on BroadleafCommerce (2 985 `.java`) with a clean tree: **0,47 s, `model_built: false`** — the short circuit (C3-32 / C3-102 / AS-13) fires, and the row's stated acceptance (`< 2 000 ms`, `model_built: false`, `verdict: pass`) already held. The reported shape reproduces exactly with **one untracked `zz.yml`**: both lists empty, `model_built: true`, **16,3 s**. `_can_move_a_verdict` admits any suffix an axis may read — deliberately, since C3-14 — so a `.yml` that is not `application*` is neither a changed `.java` nor a changed build file, and appeared in **no** published list. The change set was never empty; the payload could not say so. `analysis.model_forced_by: {count, paths, reason}` is that sentence, and `basis` no longer reads *"0 changed .java file(s) and 0 changed build file(s)"* beside two full builds. Regression `tests/test_verify_edit_names_what_forced_the_model_b4.py`, 5 assertions, including the negative control that a changed `.java` file is not explained twice. ⚠ Still open and now separable: the two-build cost itself when a forcing path exists (the parse cache serves the parse; the IR assembly is paid twice), tracked as the profiling half. ⚠ **Re-reported by the 5.8.7 audit (round 11, `BUG-06`): 145,6 s to answer `verdict: pass`, `changed_files: []` on a tree whose only entry is one untracked `.claude/settings.json`.** The row is **not reopened on that number alone**, for two stated reasons: the second measurement offered against it (63,3 s under `ASK_MAX_ANALYSIS_SECONDS=5`) says nothing, because the cap does not bound this command (`B24`); and 145 s on a 3 342-file repository is the cost of two builds, which is **this row's own reproducer**, measured at 16,3 s for a single untracked `.yml`. The check that decides it, before any tuning: whether `_can_move_a_verdict` admits `.claude/settings.json` — if it does, `model_forced_by` must have travelled in that payload and the open half is the cost, which is the profiling half above; if it did not travel, then the C1-27 exclusion (*a change under an editor or agent state directory is not a change to the code*) that `cold-start` applies through `baseline_autocapture.worktree_dirty_detail` is **not** the authority this command reads, and that is one fact with two authorities rather than a performance row. Measure it under `P1-proc` (six passes plus the control command), never from a single field clock on a cache that was cold at the start of that session. |
|
|
44
84
|
| ASK-09 / B16-sv | **Three output-ceiling guards written outside the authority that owns them.** Reported as `endpoints`, `cold-start` and `posture` claiming *"No smaller inline variant is available with the current flags"* while `--compact` fits (10 194 B ≈ 2,5 K tokens). | 5.7.2 audits (corpus `ASK-09`, `saint-server` `B16-sv` — one row, two origin IDs) | High (corpus: medium) | **closed 5.8.5 — the fifth cycle diagnosed by measurement instead of by hypothesis, and the derivation given a second authority that no thread boundary can take away.** The ledger asked for the difference between our 241 041 tokens and the reporter's 241 394 to be found in *the environment or the entry point*, and for no third fixture. It was found in the entry point, and it was measured here, on `HEAD` before the fix: `output_ceiling.build_hint(None)` — the guard reached with **no Click context** — returned *"No smaller inline variant is available with the current flags; --output FILE …, ASK_MAX_OUTPUT_TOKENS=0 …"*, the field's sentence byte for byte, about a command that declares two bounding flags. `_declared_and_used(None)` answered `([], set())` and the prefix rule spelled that **absence of a reading** as a **claim about the command**. Nothing about the repository, the size or the command was ever the subject; a `CliRunner` fixture always has a context, which is why nine batteries stayed green across three cycles. **Four things close it.** (1) **A second, context-free authority**: `output_ceiling.resolve()` reads Click's context first — the only source that can tell `--limit 50` from the 50 nobody typed — and the **command tree resolved from `sys.argv`** second, consulted only when the context produced no bounding flag at all, which is the signature of both failures the row suspects (no context; or a context pointing at a group or wrapper whose `params` are not the leaf's). The tree is registered by the entry point as a *provider*, so the Typer→Click conversion is paid only by a run already over the ceiling, and never on the answer path. (2) **Observability, which is the half the ledger said ends the cycle**: every `OUTPUT_TOO_LARGE` refusal — the emit seam, `repo-ir`'s stdout guard, `repo-ir`'s MB guard and `migrate-check`, all four — publishes `hint_basis: {derived_from, command, declared_flags, flags_already_passed, context_found}`, so **one** field run names which authority answered and what it read, instead of a fourth round of guessing. Enforced structurally: an `OUTPUT_TOO_LARGE` refusal in `cli.py` with no `hint_basis` fails the battery (AST-checked). (3) **A failed derivation fails loudly**: with nothing resolved, the hint opens *"The flags that would bound this answer could not be read from the running invocation, so this list may be missing a smaller inline view — check `--help` for the command"* and keeps the universal escapes, which hold whatever the command is. The *"no smaller inline variant"* sentence is now reserved for what it claims: a command that **was** read and declares nothing bounding, or whose bounding flags this run already passed. (4) **The collateral the row named**: `_flag_was_given` — which governs the `--force` bypass and the `--limit`-versus-`--output` reading — delegates to the same authority (`flag_was_passed`), so a run that typed `--force` and reached the guard off-context is no longer refused with its bypass silently removed. **Measured, before and after, through the real entry point** (`run_cli.py`, real ceiling, `click.get_current_context` degraded to `None` for the ceiling module only — the shape of the hypothesis, not a stub of the answer): before, the field's sentence; after, *"Use --compact …, --limit N …"* with `derived_from: "argv"`, `context_found: false`, and `--force` no longer refused on the same route. **And on the field's own subject**, `tutorials` @24 074 `.java`, no `ASK_MAX_*`, the real 50 K ceiling: `estimated_tokens: 241 527` — 0,06 % from the reporter's 241 394, so the same tree and the same payload — hint *"Use --compact …, --limit N …"*, `hint_basis.derived_from: "click"`, `context_found: true`. That is the reading this platform gives; the reporter's platform is where it degrades, and the refusal now carries the field that says which. Regression `tests/test_ceiling_hint_without_context_ask09.py`, 10 assertions, **verified red against `HEAD`** (6 failed, 4 errored) — the condition no `CliRunner` can produce, asserted end-to-end in a subprocess. ⚠ **What is still owed, and it is not a code change**: the acceptance run on Windows 11 / pipx against the real clone. It is no longer a blocking unknown — if the sentence reappears there, `hint_basis` names the cause in one run. **History of the reopening, kept because the row was closed twice on it:** **reopened 5.8.4 — fifth cycle, and the second closure declared against an artefact instead of against the reported subject.** Both 5.8.4 re-audits reproduce it, independently, on different machines. Corpus, Windows 11 / pipx / Git Bash, `tutorials` (24 073 `.java`, ~600 Maven modules), no `ASK_MAX_*` in the environment, no `tutorials/.ask`, the real 50K ceiling: `ask endpoints tutorials` exits 1 with the byte-identical *"No smaller inline variant is available with the current flags"* at `estimated_tokens: 241 394`, while `--compact` (44 973 B ≈ 11,2K) and `--limit 50` (65 038 B ≈ 16,3K) both exit 0 with 3–4× of margin and are both declared on `endpoints`. `saint-server` round 9 reports the same sentence from `cold-start` and `endpoints`, with `--compact` measured at 76 053 B and 10 230 B — **0,4 % of the payload the hint tells the caller to dump**. **The number is what makes this diagnosable at last**: our closure measured 241 041 tokens on the tree the reporter measures 241 394 on — 0,15 % apart, so it is the same repository and the same payload, and the difference lives in the environment or the entry point, not in the subject. That difference IS the finding. ⚠ **Do not build a third fixture.** Hypotheses for the agent that fixes this, cheapest discriminator first: (1) `exceeds()` calls `build_hint()` with no context, so `_declared_and_used` recovers it from `click.get_current_context(silent=True)`, which is **thread-local** — any emission that reaches the guard off the invoking thread, after the context is popped, or through a shim entry point sees `ctx is None`, gets `([], set())` back, and the hint degrades **silently** into the false sentence; a `CliRunner` fixture always has a context, which is exactly why every battery we own is green; (2) `ctx.command` resolving to a wrapper or group rather than the leaf command, so its `params` do not carry `--compact` — the reported environment is a pipx console script on Windows, which no run of ours covers; (3) a short-circuit deciding the hint before `ctx.command.params` is consulted, reachable only above a size no fixture reaches. **The fix that ends the cycle is not the sentence, it is the observability**: the `OUTPUT_TOO_LARGE` payload must publish what the hint was derived from — `declared_flags`, `flags_already_passed`, and whether a context was found at all — so one field run names the cause instead of a fourth round of guessing, and a context-less hint must fail loudly rather than claim *"nothing smaller"*, which is a confident falsehood in the exact sense this repository forbids. ⚠ The same context lookup governs `_flag_was_given("force")`, so the `--force` bypass is suspected to fail on the identical path and has never been exercised in the field. **Acceptance, unchanged and now carrying a platform**: `ask endpoints tutorials` with no environment variable names `--limit` and `--compact`, verified on Windows/pipx against a real clone (`git clone --depth 50 --single-branch https://github.com/eugenp/tutorials.git`), never against a synthetic repository. **What the 5.8.4 closure did buy, and it is real: the battery, and the `repo-ir` residue it uncovered.** History of both closures, kept because the row was closed twice on them: `tutorials` (24 073 `.java`), no environment variable, the real 50K ceiling: `endpoints` answers *"Use --compact …, --limit N …"* at `estimated_tokens: 241 041`, `cold-start` answers *"Use --compact …"* at 230 518, and `posture` does not arise — 134 431 B ≈ 33K tokens, `exit 0`, no hint at all. Three cycles of byte-identical text were measured against a build, not against this tree. **What the row's rewritten acceptance did buy is the battery**: `tests/test_ceiling_hint_at_real_scale_ask09.py` builds a repository that genuinely overflows (1 200 handler mappings, ~107K tokens), *discovers* the population by running every path-only command that declares a bounding flag rather than listing them, and asserts that no such command denies a flag it declares — parametrised by (command, repository), never by (command, artificial ceiling). It is verified red: forcing the *"nothing smaller"* prefix fails it. It also refuses to be vacuous — the fixture must overflow, the population must contain the two commands the field named, and the flag the hint offers must actually get the answer under the ceiling when run. ⚠ **The residue the old fixture could not see**: `repo-ir`'s stdout guard compared against a constant bound at import, so `ASK_MAX_OUTPUT_TOKENS=0` — which `breaking-changes-v1` declares as restoring pre-5.7.0 behaviour *exactly*, and which `repo-ir`'s own hint offers — left it refusing while every other command printed, and its refusal published no `estimated_tokens`, no `ceiling_tokens` and no `estimate_basis`, so the number it refused on existed only inside an English sentence. Both now come from `output_ceiling.exceeds`, the authority: one refusal, one shape, one ceiling that the caller can actually move. Measured: `=0` now exits 0, a lowered ceiling bites, and the payload carries its measurement. **History, kept because the row was closed twice on it:** REOPENED 5.8.2 — the closure was verified against the wrong path: against the unified emit seam, with the ceiling forced, `endpoints` answers *"Use --compact …, --limit N …"* and `cold-start` *"Use --compact …"* — the derivation reads `ctx.command.params` and works. What did reproduce is the half the row itself names: `repo-ir` carried **two** hand-written hints (stdout and the separate MB guard on a written file) and `migrate-check` a **third**, `_migrate_check_output_hint` — a hand-maintained copy of `build_hint`, same list, same prefix rule. A hand-written escape list cannot know which flags the caller already passed, which is the reported defect seen from the other side. `output_ceiling` now owns the whole vocabulary: `--force` as a **bypass** (offered only where the command declares it), `--gzip` as a **file-only** bound, `ways_out(destination="file")` for the MB guard — where `--output FILE` is not an escape and the token env var does not apply — and the *"nothing smaller"* prefix decided by counting the **bounding** escapes rather than the sentence, so a command whose only remaining escape is `--force` no longer opens with "Use". `_migrate_check_output_hint` is deleted. Regression `tests/test_ceiling_hint_one_authority_ask09.py`, 7 assertions, two of which are structural: `cli.py` may not contain the prefix sentence, and every `OUTPUT_TOO_LARGE` hint in it must come from `build_hint` (AST-checked). ⚠ **Reopened by the 5.8.2 re-audits, which report it as `B16` (saint-server) and `ASK-09` (corpus) — one defect, two origin IDs, like `B16-sv` before it.** The fix landed and the *hint* half did not: `ask endpoints tutorials` (24 073 `.java`) still answers *"No smaller inline variant is available with the current flags"* at `estimated_tokens: 240 908`, while `--compact` (45 902 B ≈ 11,5 K) and `--limit 50` (66 765 B ≈ 16,7 K) both fit with 4× of margin and are both declared on `endpoints`. Byte-identical text since 5.7.2, third cycle. **The diagnosis is the valuable part and it is about our method, not the code**: the regression assertion forces a low ceiling (`ASK_MAX_OUTPUT_TOKENS=1`) over a *small* fixture payload, and the path that fails is the *natural* rebase of a 240 K-token payload against the real 50 K ceiling — so the battery validates one route while the defect lives in another, and a green test certified a defect the field reproduces in three consecutive cycles. `posture` received `--limit`/`--compact` in the same release this row was closed in and *does* answer correctly, which is why the derivation looked proven. Suspected: a short-circuit that decides the hint before `ctx.command.params` is consulted, reachable only at a size the fixture never reaches (compare `ASK_MAX_OUTPUT_TOKENS=2000 ask endpoints <small>` against `ask endpoints <24k-file repo>`). **Acceptance, restated so the same closure cannot repeat**: the hint names `--limit` and `--compact` with an unforced ceiling on a repository that genuinely exceeds 50 K, and the regression is parametrised by `(command, repository)` over a fixture that really overflows — never by `(command, artificial ceiling)`. ⚠ **The owed acceptance run is no longer owed: both 5.8.5 re-audits certify the closure from the outside, one of them on the exact platform the row could never reach.** Corpus cycle 8, Windows 11 / pipx / Git Bash, `tutorials`, no `ASK_MAX_*`, real 50 K ceiling: `estimated_tokens: 241 394` — the same number as the four failing cycles, so the same tree and the same payload — now answering *"Use `--compact` …, `--limit N` …, `--output FILE` …, `ASK_MAX_OUTPUT_TOKENS=0` …"*, and the reporter records the root cause as ours: *"el hint no negaba los flags: negaba haber podido leerlos, y la regla de prefijo convertía «no leí nada» en «no hay nada»"*. `saint-server` round 10 certifies the **derivation** rather than the sentence, which is the half that was not asked for: `cold-start` names `--compact` and **not** `--limit` because it declares none, `endpoints` and `validation` name both, and each offered flag is measured actually getting under the ceiling (`cold-start --compact` 76 324 B; `endpoints` 10 230 / 13 534 B; `validation` 6 982 / 10 684 B against 961 077 B unbounded, −99,3 %) — *"la introspección de Click funcionando en las dos direcciones"*, with the inverse error (naming a flag the command does not have) explicitly checked for and absent. Both reporters mark the row closed; the corpus reporter's open inventory is now empty except `R2`. |
|
|
45
85
|
| ASK-12 | **A field describing an exclusion named three production trees as excluded.** `spring-audit spring-boot` published `patterns_matched: [… "core/", "module/", "cli/" …]` — 4 254 + 1 625 + 76 production files. Read literally, whole modules had left the audit population; measured, the exclusion was *conservative* (3 650 excluded against 4 214 files actually under a test root). The auditor filed an over-exclusion false positive on the strength of the field and then withdrew it. Cause: the constructor's fallback named `parts[0]`, the first path segment, when no source-root marker matched — and the same published field had **two constructors** with different shapes and different `basis` strings (`cli.py` for `spring-audit`, `repository_ir.py` for `endpoints`) over the one authority both already shared. | 5.7.2 audits | Low — a reporting defect, not an exclusion one, which is why it cost an auditor a round instead of costing a finding | **closed 5.8.1** — one producer, `repository_ir.excluded_test_source_detail`, beside `is_test_source_file`, which is the authority that decides the fact. `patterns_matched` can now only hold a **declared test root** — a directory whose files are excluded in their entirety — and everything else is published as a `rules_matched` census with a `rules_meaning` sentence per rule, so a per-file rule can no longer be read as a whole-directory claim. A surface counting something other than files says so (`unit`, `files_matched`), because `endpoints` counts route annotations. Regression `tests/test_test_source_exclusion_one_producer_ask12.py`, 14 assertions, two of them structural: `cli.py` may not compose the key at all, and exactly one function in `repository_ir.py` may (AST-checked). |
|
|
46
86
|
| B14 | **`--compact` made the answer smaller by making it unattributable.** `cold-start --compact` took 1 968 807 characters to 16 452 (−99,2 %) and removed **11 of 19 keys** on the way — `schema_version`, `repo_id`, `current_git_head` and the 331-character `hotspots` among them — in a document whose weight was **97,7 % `endpoints`**, and cut the endpoint list to 30 with no `*_cap`. The view an agent is told to inject could not say which repository, which commit or which schema it described. | 5.6.1 audit; AS-15 closed the root `--compact` half in 5.7.0, this is the `cold-start` half, re-reported against 5.8.0 | High | **closed 5.8.1** — three rules, in the shared bound rather than invented per command. `COMPACT_IMMUTABLE_KEYS` is the minimum no `--compact` may remove (provenance is contract, not content). Every collection that was cut declares `{total, shown, omitted}` with the registered effect — measured on BroadleafCommerce, `endpoints_cap` now publishes `total: 272, shown: 30, omitted: 242` where the cut was silent. And trimming goes by **weight**: a section already under 2 KB is kept rather than dropped by a fixed list, which is what recovers `hotspots`; everything actually removed is named with its size in `compact_omitted`. Regression `tests/test_compact_keeps_identity_b14.py`, 12 assertions, including that nothing removed claims nothing. |
|
|
@@ -67,6 +107,16 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
|
|
|
67
107
|
| E-37 | **Two environment variables move three cache stores, and no surface said which moves which.** `SOURCECODE_CACHE_DIR` relocates the per-repository snapshot store (core snapshots, the rendered L2 view, the RIS); `SOURCECODE_CONTEXT_CACHE_DIR` relocates the shared ones (the Canonical IR and the per-file parse store). Their defaults are siblings under `~/.sourcecode`, so the split is not derivable from the paths, and `cache status` published a path per store with no variable beside it while `docs/CACHE.md` named both variables in one sentence after the table. | found here 2026-08-18 while taking the controlled A/B `ASK-17` owed, not reported by the field | Medium — it does not make an answer wrong, it makes a **measurement** wrong, silently: an operator who redirects or empties *the cache* moves one store and is served answers out of the other | **closed 5.8.5** — measured first: with only `SOURCECODE_CONTEXT_CACHE_DIR` redirected, the second cold pass of each version answered from the L2 view its own first pass had written — **2,4 s against 113 s, with an empty parse store** — and the run reported `cache_source: L2_view` without anything naming the store it came out of. Now every store in `cache status` publishes `base_env`, from one authority (`cache._STORE_BASE_ENV`), the text answer prints *«<path> ($VAR moves it)»* beside each one, `stores.base_env_note` states the consequence rather than the layout, and `docs/CACHE.md` carries a **column** instead of a sentence covering both. Regression `tests/test_cache_store_base_env_e37.py`, 8 assertions, and the ones that matter are **behavioural**: setting the variable a store names moves *that* store and leaves the others where they were — a published mapping nobody exercises is the class of claim this repository refuses everywhere else. The CLI is forbidden a second copy of the mapping (structural assertion). **Confirmed from the outside in cycle 8, by the reporter falling into it first**: isolating only `SOURCECODE_CONTEXT_CACHE_DIR`, their second *cold* pass answered from the L2 view their first pass had written — **2,4 s against 113 s with an empty parse store** — and their write-up now quotes the shipped `base_env` mapping back as the fix (*"dos variables mueven tres almacenes y ninguna superficie decía cuál"*). A defect found while measuring, that was itself corrupting the measurement. |
|
|
68
108
|
| B21 | **Six of the thirteen shapes being renamed name no command that emits them.** `schemas-v1` publishes `emitted_by` per shape; for `migration-blast-v1`, `spring-impact-v1`, `event-topology-v1`, `test-gap-ranking-v1`, `canonical-ir-v1` and `hibernate-strategy-v2` it is `[]`, beside seven shapes where it is populated. Read the way a consumer reads a list, an empty `emitted_by` says *nothing emits this shape* — which tells the one consumer that **is** affected by `BC-002` that it is not. | found here while declaring `BC-002` (5.8.5), not reported by the field | Low — a disclosure gap in a declaration whose whole purpose is to let a consumer decide whether it is affected | **closed 5.8.6 — derived, not typed out.** `schema_producers` reads the two authorities the CLI already holds — the registered command tree and the import graph of the module that defines each callback — and answers with the evidence it derived on: `command_import` (the command's own body imports the producing module), `module_import` (one hop, through an intermediate narrow enough to attribute), `substrate` (reached from more commands than a producer would be) or `unresolved` (nothing found, said in a sentence). Five of the six now name a command — `migration-blast-v1` → `migrate-check`, `spring-impact-v1` and `event-topology-v1` → `impact-chain`, `test-gap-ranking-v1` → `impact`, `hibernate-strategy-v2` → `migrate-check`/`migrate-recipe` — and the sixth, `canonical-ir-v1`, is reached from **28 modules**: naming every command would be less true than naming none, so it publishes `substrate` with the count and the instruction to match on `subject`. A declared producer still wins, because some producers are not one registered command (`ask (root)`, `baseline capture`). Regression `tests/test_schema_producers_derived_b21.py`, 33 assertions, parametrised by the registry so a fourteenth unnamed shape fails the battery. **History:** open, and named rather than shipped as a fact: every `BC-002` row with an empty producer list carries `emitted_by_basis` — *«not declared in the registry: this shape's producing command is not named yet, so match on `subject`»* — so the gap is visible instead of being read as a negative claim. What remains is to fill the six, and the honest way is derivation rather than six more hand-written strings: the producer is discoverable from the command that constructs each payload, which is the same authority every other population in this repository comes from. Until then the assertion in `tests/test_schema_rename_window_b20.py` holds the weaker invariant a reader can rely on: a row either names its emitters or says why it cannot. |
|
|
69
109
|
| C3-129 | **`timeline`'s absolute cost, now fully attributed and still 27 % above its own record.** `C3-128` closed by instrumenting rather than tuning, which was the right order and left this behind: the clock is split, nothing is unaccounted, and the number has not moved. Round 10 on 5.8.5 measures `timeline --since HEAD~5` at **~198 000 ms against the 5.7.2 record of 155 950 ms (+27 %)** — the only gate-shaped command over 120 s that is not there structurally (`delta` 191 s and `contract-diff` 115 s walk two whole trees, `compare` 46 s walks N candidates; `timeline` walks **five** trees and costs more than `delta` does with two). | 5.8.2 → 5.8.5 re-audits (residual of `C3-128`, which closed the attribution half in 5.8.5) | Low-Medium — an investigation command rather than a CI gate, and the last performance figure of the round sitting outside its own best | **closed 5.8.6 — by not paying twice for what does not change.** A metric at a commit is a pure function of that tree, and the tree at a sha never changes: the only cache in this product whose key can be exact rather than heuristic (analyzer fingerprint + metric + commit sha). `timeline_cache.SampleCache` stores measured values under the per-repository core store, and when every watched metric for a sample is a hit the tree is **never materialised**, which is the 13,8 % the split attributes to git. Measured on BroadleafCommerce over the same range: **26 845 ms → 12 ms**, samples and transitions byte-identical. What makes it admissible rather than merely fast, all asserted: a failed sample is never stored (one bad run cannot become permanent), the fingerprint is in the key (corrected analysis is never served from a previous release), the hit is published (`sample_cache`, `from_cache` per sample, and `cost` separates the cached population from the measured one instead of reporting a median over both), and `--no-cache` / `ASK_TIMELINE_NO_SAMPLE_CACHE=1` measure everything again so the command stays measurable without emptying a store (E-37). `cache_model` carries the layer, so `cache model` and `docs/CACHE.md` state it from one authority. Regression `tests/test_timeline_sample_cache_c3_129.py`, 11 assertions. **History:** open — and it opened with the answer already published, which is why it is cheap to attack and expensive to leave: `timeline`'s own `timings` say **82,9 % `measure:posture`, 13,8 % `materialise_tree`, 3,3 % `release_tree`, 0,0 % unattributed**. So this is not a `timeline` defect at all: it is `posture` paid five times over five materialised trees, and it makes the same substrate question `C3-127` asks — with the difference that here the five payers are one command, so the cache is intra-invocation and needs no cross-command contract. ⚠ **Do not reopen `B7`**: cross-tree cache reuse is healthy, the v3 assertion passes at 6,8×. Acceptance: `timeline --since HEAD~5` back inside +10 % of 155 950 ms on the audited corpus with `timings` still summing to 100 %, and `measure:posture` falling as a share rather than the total falling for an unnamed reason. |
|
|
110
|
+
| B23 | **The self-diagnostic's `ASK-09` row reads the refusal at the wrong nesting level, so it can only fail for reasons that are not the row.** Field: `ask selftest .` publishes `{"row":"ASK-09","status":"fail","seconds":4.42,"observed":"the refusal carries no `hint_basis`"}` while the same build, the same repository and the same session produce a refusal that carries `hint_basis` with `derived_from`, `command`, `declared_flags`, `flags_already_passed` and `context_found`. | 5.8.7 audit (round 11, `BUG-01`) | High — a ledger that runs itself is `F-BV`'s whole premise, and one false positive costs the five rows that pass | **open** — mechanism read at HEAD, not inferred: `_check_ask09` (`selftest.py:119-151`) runs `endpoints <repo>`, treats **any** non-zero exit as the refusal, and reads `hint` from the envelope root while the emitter nests it under `error` (measured here with `ASK_MAX_OUTPUT_TOKENS=1` on `tests/fixtures/test_gap_ranking`: `error.hint` present, root `hint` absent, root `hint_basis` present). Two consequences, and the second is worse than the report's: the first branch — *the refusal denies a smaller view while declaring flags*, the half that would catch the defect the row exists for — **can never fire**, because the string it matches is in a key it never reads; and any non-zero exit that is **not** `OUTPUT_TOO_LARGE` (a `--no-write` refusal, an invalid input, a crash) falls to the `not basis` branch and is published as `ASK-09 fail`. The field's 4,42 s against an `endpoints` that costs 6,5 s warm on that repository is consistent with the second path, and the probe cannot tell the reader which it took. Acceptance: the probe asserts the observed `error.code` **is** `OUTPUT_TOO_LARGE` before evaluating anything else, answers `not_applicable` **naming the code it saw** when it is not, reads `hint`/`hint_basis` from the document the emit seam actually writes, and carries one battery case per branch — fits under the ceiling · refuses correctly · refuses badly · failed for another reason. The last case is the regression: a row that cannot distinguish *the assertion is false* from *the run did not happen* is not a ledger. |
|
|
111
|
+
| B24 | **`ASK_MAX_ANALYSIS_SECONDS` is published as the CI budget control and bounds three commands.** Field, all at cap 5 s: `risk` honours it exactly (8,5 s, `PARTIAL, budget exhausted`, and 180 s → 180,7 s), while `timeline --since` runs 58,2 s, `verify-edit` 63,3 s, `contract-diff` 87,9 s at cap 10, and `enrich --sarif` and `cache warm` were killed above 200 s — every one `rc=0` and complete. | 5.8.7 audit (round 11, `BUG-02`) | High — the round's stated conclusion is *«si querías gatear CI con un budget, no puedes»*, and that is the use the surface advertises | **open** — ⚠ **correction to the reported mechanism, read at HEAD: nothing ignores a check, because on those paths there is no check.** `_analysis_budget` (`cli.py:1566`) resolves the variable and `_enforce_analysis_budget` (`cli.py:1622`) **warns and never bounds** — that is C3-55, decided and correct: below the class floor was once *rejected* and above it admitted a run that killed the session, so the floor is advice. Bounding lives in `phased_run`, and only three commands hand it a deadline: `spring-audit` (`cli.py:9699`), `risk` (`cli.py:10630`) and `audit-report` (`cli.py:11008`). The budget is therefore a **phase-runner** contract published as a process-wide one — `cache_model.py:657` prints it as *«bound and narrate a run where CI wants an explicit budget»* with no scope beside it. Acceptance, either half: the CIR build itself honours a deadline, so a budget bounds every repository-wide command and a truncated build declares itself the way `risk` does; **or** the surface names the population it bounds, derived from the registry rather than a hand list, and every command outside it publishes `analysis_budget.applies: false` with the reason. Until one of the two ships, no gate can be timed by this variable, and the help must not imply it can. |
|
|
112
|
+
| B25 | **`data-exposure` cannot tell *no labels declared* from *labels declared and unusable*.** The same malformed declaration (`dataLabels` as a dict, `customSecurityAnnotations` as a list of strings) is diagnosed exactly by `ask config . --config X.json` — *«`dataLabels`: not_a_list — expected a list of label declarations, found dict»*, *«declared and unusable»* — and reported by `ask data-exposure . --config X.json` as *«no labels declared — nothing measured»*. | 5.8.7 audit (round 11, `BUG-03`) | Medium — the user declares labels, is told nothing was declared, and concludes the differential feature of the product does not work | **open** — the validator exists and already produces the right sentences; what is missing is that its **result**, not its count, reaches the payload. `data-exposure` evaluates an emptied list after the parser has dropped the invalid entries, and loses the reason with them. Acceptance: a declaration that parses to zero usable labels answers `zero_result_reason: "declaration_invalid"` and carries the same warnings `config` prints, keyed to the same declaration; a declaration that is genuinely absent keeps today's sentence. One authority for *is this declaration usable*, two readers. |
|
|
113
|
+
| B26 | **The first number a new user reads is labelled `all languages` and is below the single-language count of the same tree.** `ask onboard .` and `ask prepare-context onboard .` say *«3 337 source files (all languages)»* on a repository where four independent methods count **3 342** `.java` (`find`, `git ls-files`, `find ./src`) and where `cache warm`, `selftest` and `spring-audit` all publish 3 342 — with 3 317 `.ts` in a sibling module besides. | 5.8.7 audit (round 11, `BUG-04`) | Medium — low functionally, high in trust: it is the first figure a newcomer sees and it is contradictable in five seconds | **open** — ⚠ **the reported cause is already decided and is not the defect.** C1-19 ruled that these are three legitimate populations, and `migrate-check.metadata.files_scanned_basis` names all three by their figures (3 337 / 3 336 / 3 342) precisely so they stop reading as drift. What survives is narrower and is real on both halves: the **label** is false — a count that excludes anything is not *all languages*, and one arithmetically below a single language's count in the same tree cannot be it (`summarizer.py:154-156`) — and the **basis lives in another command's payload**, so the two commands a newcomer runs first are the two that publish the figure without it. Acceptance: the sentence names the population it counted (or counts every language and says so), and the basis travels beside the number in the command that prints it, not only in `migrate-check`. |
|
|
114
|
+
| B27 | **The route into an external baseline history exists and no surface names it.** Under `ASK_READONLY=1` the round concluded that *«la única función de series temporales exige contaminar el repositorio»*: `ask baseline capture . --dir /external` writes, `ask baseline diff . --baseline /external/<sha>.json` compares, and `ask baseline trend . --dir /external` / `--baselines /external` answer `INVALID_INPUT: No such option`. | 5.8.7 audit (round 11, `BUG-05`) | Low — reported High; downgraded on measurement, with the disclosure half intact | **open (discoverability), premise refuted.** `trend` and `baseline trend` take the history directory **as their positional argument**: `_resolve_history_dir` (`cli.py:8508-8523`) uses a directory that already holds baselines as it is, so `ask trend /external` is exactly the read the round wanted and the time series is reachable without writing a byte into the repository. `--dir` is a spelling those two commands do not have, not a capability they lack. What is defective is the surface on both ends: the root help publishes the location as *«`<repo>/.ask/baselines/` (or `--dir`)»*, which reads as a flag every baseline command takes; and `trend`'s own `INVALID_INPUT` for an empty history names `ask baseline capture` and never the directory route it accepts. Acceptance: the two commands accept `--dir` as an alias of the positional (C4-25's rule — an alias, never a rename), and the refusal names the external-directory route it already supports. This is also the residual of **F-AX**: the auditor-on-somebody-else's-repository case is served, and reads as unserved. |
|
|
115
|
+
| B28 | **`selftest` publishes the platform its verdicts were measured on and calls Windows 11 Windows 10.** Field: `Windows 10 10.0.26200` against a real Windows 11 Pro 10.0.26200. | 5.8.7 audit (round 11, `BUG-10`) | Informational — and it lands on the one field that exists because `ASK-09`'s difference was environmental | **open** — `selftest.py:389` builds the subject from `platform.release()`, which returns `'10'` on Windows 11; the build number in `platform.version()` is the discriminator (`>= 22000` is 11). `F-BV` publishes the subject *because* a verdict without its platform is what let one row be closed twice against the wrong environment; a platform stated wrongly weakens exactly that argument. Remedy: derive from the build number, and keep the raw `platform.release()` beside it rather than replacing it. |
|
|
116
|
+
| C1-49 | **A summary field named after the population it does not count, beside a note that counts it.** `validation.summary.endpoints_with_body: 0` on a repository with **1 252** body-carrying endpoints — the same payload's `note` states the 1 252, `gaps` lists them one by one with `reason: body_endpoint_without_declared_constraints`, and 840 `@RequestBody` were verified by hand in the source. | 5.8.7 audit (round 11, `BUG-11`) | Medium — the behaviour is correct and documented; the name is the defect, and a name is what a consumer reads | **open** — the reconciliation half shipped as DR-4 (`45631a1`, `validation_pattern.scope_note`: the summary counts the *declared-constraint* surface, the pattern counts the body surface), and the round confirms the prose is right. What no `scope_note` can fix is `jq '.summary.endpoints_with_body'` answering `0` on a repository with 1 252 of them, which is the dashboard case this product exists to be trusted in. Acceptance: publish `endpoints_with_declared_constraints` as the literal name of what that field counts and `endpoints_with_body` with its literal meaning; the old key stays until the window says otherwise, because renaming an emitted key in silence is the incompatible change `ASK-11` and `BC-002` exist to govern — this goes through `breaking-changes-v1` with a target version, like every other one. |
|
|
117
|
+
| C3-130 | **`plan` and `cold-start` pay the whole analysis and then refuse to print it.** `ask plan IGenericService .` → 35,4 s consumed, then `exit 1` with `OUTPUT_TOO_LARGE` (*«Estimated output is ~77K tokens … No smaller inline variant is available with the current flags; --output FILE to save»*); the identical run with `-o` costs **30,8 s** and writes 311 647 B. | 5.8.7 audit (round 11, `BUG-12`) | Medium — the work is done, correct, and thrown away, on the reading of the tool's own size policy | **open** — `repo-ir` already does the right thing and is the precedent: it estimates from file count × bytes-per-file and refuses in 0,6 s with a stated band (C3-18, C3-111). `plan` and `cold-start` measure the payload **after** serialising it, so the refusal costs the run. Acceptance, in preference order: (a) where no smaller variant exists and the only missing thing is a destination, write to a temporary file and name the path — the reader gets the answer instead of losing 35 s; (b) failing that, an ordering estimate before the analysis, from a population the command already knows (matched symbols × mean bytes), with its basis published the way `repo-ir`'s band is. Either way the estimate is a floor with its basis, never a confident byte count. |
|
|
118
|
+
| C4-27 | **A fuzzy flag suggestion that points at an unrelated flag is worse than none.** `ask plan IGenericService --path .` → `INVALID_INPUT: No such option: --path (Possible options: --depth)`. Edit distance 3 over 6 characters, and no semantic relation whatsoever. | 5.8.7 audit (round 11, `BUG-08`) | Low | **open** — threshold the suggestion (distance ≤ 2 and a shared prefix or concept) and omit the parenthesis when nothing qualifies. In this exact case the correct sentence is the one C4-25 owns: *`--path` does not exist here; the repository is the positional argument* — so the suggester should defer to the grammar authority before guessing at spelling. |
|
|
119
|
+
| C4-28 | **One write in the catalogue confirms itself with nothing on stdout.** `ask fix-bug . -o out.json` → stdout 0 bytes, stderr 0 bytes, `rc=0`, and 50 450 bytes correctly on disk, while `ask verify . -o`, `ask pr-impact . -o` and `ask baseline capture . --dir` each print a sentence naming the path and the headline count. | 5.8.7 audit (round 11, `BUG-09`) | Low — a script that treats non-empty stdout as the success signal fails silently on this one command | **open** — and the population is four commands, not one: 41 of the 57 `_emit_command_output` call sites pass a `success_msg`; the shared `prepare-context` seam (`cli.py:6358`) passes none, and `fix-bug`, `onboard` and `review-pr` all delegate to it. The C3-122 fallback receipt does fire, but it is a `_notice` — stderr and TTY-gated **by decision** (C3-26: a stderr write from a native command breaks a PowerShell capture), so off-TTY silence is what it was designed to produce. Acceptance: the seam carries a sentence per task on stdout, like every other write, and the affordance battery derives its population from the registry so a new task on that seam cannot ship without one. |
|
|
70
120
|
|
|
71
121
|
---
|
|
72
122
|
|
|
@@ -321,7 +371,7 @@ is now the lowest axis (3/10), below every analytical one.*
|
|
|
321
371
|
| C4-22 | **`ask schema <unknown>` publishes an inventory of its own argument that is missing half of it.** `ask schema endpoints` answers `INVALID_INPUT` with `"available": ["envelope-v1"]`, while `ask schema` — the command its own hint sends the reader to — lists `schemas: ["envelope-v1"]` **and** `registries: ["facts-v1"]`, and `ask schema facts-v1` resolves and prints the fact registry. So the error's list is not the set of values the argument accepts: an operator who reads it concludes, as this one did, that *"el único schema publicado es envelope-v1"*. Reproduced at HEAD | 4.15.0 (eval #20) | Low — but it is the ledger's own subject matter, a surface describing itself wrongly, and `facts-v1` is the registry this ledger tells readers to consult | **closed 4.17.0** — the registry names itself (`registry_version`), so the listing, the dispatch and the rejection read one value and none carries a literal: the message says *schema or registry*, `expected` enumerates both and the hint quotes them. SC-02 asserts the two surfaces agree, so a second registry cannot be listed and left out of the rejection |
|
|
322
372
|
| C4-23 | **One guard, two units: the error says 50 MB and the flag that bypasses it says 50 K tokens.** `repo-ir` refuses with *"Estimated output is 34-96MB for 3339 files, above the 50MB ceiling for a written IR"*, and `--force` documents itself as *"Bypass the token-size guard … even when estimated tokens exceed 50K."* A reader cannot size `--max-nodes` without running the command and reading the error | 5.3.1 (audit #28) | Low | **closed 5.4.0** — both, and the conversion is that there is none: they are two guards over two destinations, a context window and a disk, and both are named from their constants wherever either is. The stdout ceiling got a name (`_IR_STDOUT_CEILING_TOKENS`) instead of a literal at the branch. Original remedy note: one unit, taken from the guard that actually fires; if both are real, publish both and the conversion between them |
|
|
323
373
|
| C4-24 | **Two documents this build points at as authorities are not distributed with it.** `ask --help` sends the reader to *"full reference in docs/USER_GUIDE.md"* and `ask version` publishes `build.known_defects: "docs/DEFECT-LEDGER.md records what this version is known to get wrong"`. Neither file ships in the package. The ledger is the sharper miss: it is offered as the authority on the known defects of the installed version, by a product whose central claim is traceability of what it knows and does not know | 5.3.1 (audit #28) | Low-Medium — ninth appearance of the C4 shape, and the first where the unreachable document is this one | **closed 5.4.0** — hatch `force-include` puts the repository's own file in the wheel (copied by the build, so there is no second copy to drift) and `sourcecode.bundled_docs` resolves the pointer from what is packaged, saying so when a build does not carry it. The battery reads the wheel manifest, so a document a surface names and the package stops shipping fails the build. Verified by extracting the built wheel. Original remedy note: ship them in the wheel, or point at a URL the installed build can resolve; and derive the pointer from what is packaged, so a document that stops shipping fails the battery rather than the reader (the C4-1 rule) |
|
|
324
|
-
| C4-25 | **One concept, seven spellings — and it produced 5 of the evaluator's 11 usage errors.** The same idea (*which tree, which ref, which subject*) is spelled `--since`, `--base-ref`/`--head-ref`, `--dir`, `-b <file>`, a bare positional, `--files` and `-t` across sibling commands. This is the residue P2-G named and left open (*unify arg conventions*), now measured from outside: the most experienced possible reader, running the full catalogue with the help in front of them, got the grammar wrong on five commands | 5.3.1 (audit #28) | Medium — 98 % catalogue coverage was reached in spite of the grammar, and every one of those errors is a first-run failure for a new user | **closed 5.4.0** — nothing renamed, every change an accepted alias: `--base-ref` also takes `--since`, `verify --baseline` also takes `-b`, `--dir` and `--history-dir` accept each other. The worse half is closed too — `--files` meant two argument shapes under one name, and `_resolve_files_option` is now the one reader for all three shapes on every command that has it. `tests/test_cli_grammar.py` derives the population from the registry and holds five declared, reasoned exceptions; a stale exemption fails as loudly as a missing alias. Original remedy note: the population comes from the CLI registry, not a hand-written table: one battery over every command's parameters asserting that a given concept carries one spelling, with the exceptions declared and reasoned (the `COMMANDS_WITHOUT_COPY` pattern). Renames keep the old spelling as a deprecated alias |
|
|
374
|
+
| C4-25 | **One concept, seven spellings — and it produced 5 of the evaluator's 11 usage errors.** The same idea (*which tree, which ref, which subject*) is spelled `--since`, `--base-ref`/`--head-ref`, `--dir`, `-b <file>`, a bare positional, `--files` and `-t` across sibling commands. This is the residue P2-G named and left open (*unify arg conventions*), now measured from outside: the most experienced possible reader, running the full catalogue with the help in front of them, got the grammar wrong on five commands | 5.3.1 (audit #28) | Medium — 98 % catalogue coverage was reached in spite of the grammar, and every one of those errors is a first-run failure for a new user | **closed 5.4.0** — nothing renamed, every change an accepted alias: `--base-ref` also takes `--since`, `verify --baseline` also takes `-b`, `--dir` and `--history-dir` accept each other. The worse half is closed too — `--files` meant two argument shapes under one name, and `_resolve_files_option` is now the one reader for all three shapes on every command that has it. `tests/test_cli_grammar.py` derives the population from the registry and holds five declared, reasoned exceptions; a stale exemption fails as loudly as a missing alias. Original remedy note: the population comes from the CLI registry, not a hand-written table: one battery over every command's parameters asserting that a given concept carries one spelling, with the exceptions declared and reasoned (the `COMMANDS_WITHOUT_COPY` pattern). Renames keep the old spelling as a deprecated alias ⚠ **Re-reported by the 5.8.7 audit (round 11, `BUG-07`) with four failed invocations, and it exposes a population hole in the battery rather than a stale exemption.** `tests/test_cli_grammar.py` walks the parameters commands **declare**, so it can only assert that a concept present under two names is unified — a command that lacks the concept entirely is invisible to it. That is exactly where the round tripped: `plan --path` and `compare --path` against a positional repository (`--path` is not in `_CANONICAL` at all), `review-pr --files` (`--files` exists on `pr-impact`, and `_resolve_files_option` reads all three shapes, but the flag was never added here), and `baseline trend --dir` (the concept is `out_dir`/`history_dir`, and `trend` declares neither — see `B27`). Acceptance for the residual: the battery asserts over **concepts × commands that take that concept's subject**, so an omission fails as loudly as a divergent spelling; `--path` joins `_CANONICAL` as an accepted alias of the repository positional; `review-pr` accepts `--files`. Aliases only — nothing renamed, per the closure above. |
|
|
325
375
|
| C4-26 | **`enrich` answers the CVE question and nothing leads a reader to it.** `trivy fs --format sarif -o cves.sarif` → `ask enrich . --sarif cves.sarif` answers *"of N CVEs, which sit in code reachable from an unauthenticated request"* — the ranking a Java 8 / Boot 2.7.18 repository needs most, since the raw list is unmanageable. Verified working in audit #28 against a synthetic SARIF: 1 of 2 results enriched, the other correctly `unresolved` with a reason. The capability is in the build, the `--help` describes the case, and no surface joins the two ends of the pipe | 5.3.1 (audit #28) | Low-Medium — the tenth appearance of *the capability exists and the surface does not lead to it* | **closed 5.4.0** — the pipeline is in the command's own help, on `--sarif` itself, and in the guide's section, with the reason it matters on a repository whose raw CVE list is unmanageable. Original remedy note: publish the pipeline where a reader meets the problem (the `enrich` help's first line, the guide's security section), with the scanner named as an example and the coordinate rule quoted from `remedies` rather than restated |
|
|
326
376
|
|
|
327
377
|
|
|
@@ -491,6 +541,7 @@ class's subject, not its provenance.
|
|
|
491
541
|
|
|
492
542
|
| Window | Recorded defects | Closed | Note |
|
|
493
543
|
|---|---|---|---|
|
|
544
|
+
| 5.8.7 → (this queue) — **audit round 11 on `saint-server`, the first round run entirely under `ASK_READONLY=1` and a proven non-persistence protocol** | the same subject (`saint-server @ 3dde0376`, 3 342 `.java`, Spring Boot 2 + MyBatis + Spring Security), **74 invocations / 2 082 s of command time**, 51 of 53 in-scope leaf surfaces executed (44 OK · 4 `OK_INÚTIL` · 3 error), plus a control run on a sibling module with 0 Java files. **8,5/10** | rows opened: **B23** (`selftest`'s own row false-positives), **B24** (the analysis budget bounds three commands and is published as global), **B25** (declared-and-unusable read as not-declared), **B26** (`all languages` below one language), **B27** (the external history route exists and nothing names it), **B28** (Windows 11 published as Windows 10), **C1-49** (`endpoints_with_body: 0` beside 1 252 of them), **C3-130** (the ceiling refuses after paying for the answer), **C4-27** (a fuzzy suggestion with no relation to the flag), **C4-28** (one write with no confirmation on stdout). Re-reports recorded in place, not duplicated: `B4` (`verify-edit` at 145,6 s on a clean tree) and `C4-25` (four grammar errors, and the battery's population hole behind them) | **Zero defects in the analysis layer, for the eleventh consecutive round.** Six independent verifications against the source, six exact: `TX-001` on the stated line 128, 38 callers against 39 grep hits minus the declaring file, 39 files in a `--dry-run` rename, `@M3FiltroSeguridad` at 2 348 uses, an endpoint's path + handler + `void`, and `spring-boot-starter-validation` present in the pom against **0** `@Valid`. **0 false positives found.** The twelve defects are all in the wrapper — a budget's scope, a grammar, a field name, a probe that reads the wrong key — which is the split this ledger has recorded since 3.2.0 (*«motor sólido, envoltorio descuidado»*), now at the wrapper's narrowest. The round also proves what earlier rounds could only assert: the repository is **byte-identical** afterwards — `git status`, `HEAD`, reflog, no `.ask/`, no config file, no hooks — with the one exception the auditor disclosed themselves, the tool's own cache outside the repository. |
|
|
494
545
|
| 5.3.1 → (this queue) — audit #28, **the first round run under a written non-persistence protocol, and the round that finds the worst defect in this ledger** | the same subject (`saint-server @ 3dde037`, branch `banyan-v2`, 3 342 Java files / 6 947 sources / 3 574 endpoints, Boot 2.7.18 / Java 8) under `ASK_READONLY=1` + `--no-write` with every output written to a scratchpad: **113 instrumented invocations, 50 of 51 non-prohibited commands (98 %), 97 exit 0, 16 non-zero of which 11 were the evaluator's own usage errors and 5 intentional product guards, zero crashes, zero tracebacks, zero hangs**, repository byte-identical at the end, and the two irreversible divergences (the cross-repository parse cache, the shared CIR) declared rather than hidden. **8,41/10** on a seven-dimension weighted rubric. 16 claims cross-verified line by line against source: **13 correct, 3 wrong** | rows opened: **E-3 · C1-44 · C1-45 · C2-32 · C3-114 · C3-115 · C3-116 · C3-117 · C3-118 · C3-119 · C4-23 · C4-24 · C4-25 · C4-26**; capabilities admitted in **Amendment U**: F-BK … F-BO; carry-over recorded as **CL-30** | **The finding is a number, and it is ours: 1 955 of 3 574 endpoints — 54,7 % of the HTTP surface — published `none_detected` with a live gate in source (E-3).** The evaluator did not report a symptom; they built a Java parser, validated it 15/15 against hand-checked cases, and produced the census that attributes the defect to one lookup — a handler whose mapping is declared on an abstract superclass is indexed under the declaration, and the gate is on the override. It propagates to eight commands, runs in the unsafe direction, and **retires this repository's own most-quoted figure**: *"2 635 endpoints with no policy"* is really ≈659. Two rows underneath it are the same shape one level down — a taint join keyed on a bare statement id that puts a non-exploitable OGNL ternary at rank #1 (C1-45), and a declaration file whose unusable keys run clean through the two fields the schema publishes for exactly that (C3-116). **Against that, what held**: 98 % catalogue coverage with no crash, every guard that fired firing correctly, and the honest-degradation culture visible in the places it cost the product a point — `generate-tests` saying in words that empty is not *nothing found* while overshooting its own budget by 88 % (C3-118). Their sentence for the feature half is the roadmap: *"un producto que se vende por decir siempre de dónde sale cada afirmación tiene su mayor punto ciego exactamente en la afirmación que más pesa"* — which is F-BN, and which would have made E-3 obvious in one command |
|
|
495
546
|
| 2.7.0 → 3.1.0 (8 weeks, 2 majors) | 8 tracked externally | **4** | Externally scored in our favour: *"software en desarrollo activo y rápido, no abandonware"*. The score did not move, because the fixes were symptom-level; see [`ADR-0008`](architecture/adr/0008-answer-coherence.md). |
|
|
496
547
|
| 3.1.0 → 3.2.0 | 4 C1 rows targeted | **4** (C1-1, C1-3, C1-4, C1-5) | First release where remediation was **fact-level** rather than symptom-level: one authority, every consumer rebound, parity asserted. The external score moved for the first time in three evaluations — 7.5 → **8.0** — and the named ceiling moved from internal consistency to documentation. C1-4 also shows why the maintenance rule exists: it had previously been marked closed while a second consumer still printed `none_detected`. |
|
sourcecode/_docs/USER_GUIDE.md
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -1580,11 +1580,16 @@ def _analysis_budget(command: str) -> dict[str, Any]:
|
|
|
1580
1580
|
# person reading `total_defects` is often not the person who set it.
|
|
1581
1581
|
# Where it came from travels with it, from here to `_partial`.
|
|
1582
1582
|
source = f"ASK_MAX_ANALYSIS_SECONDS={raw} (process environment)"
|
|
1583
|
+
from sourcecode.phased_run import budget_scope
|
|
1584
|
+
|
|
1585
|
+
scope = budget_scope(command)
|
|
1583
1586
|
return {
|
|
1584
1587
|
"class": cls,
|
|
1585
1588
|
"recommended_min_seconds": recommended,
|
|
1586
1589
|
"configured_max_seconds": configured,
|
|
1587
1590
|
"configured_source": source,
|
|
1591
|
+
"applies": scope == "phase_runner",
|
|
1592
|
+
"scope": scope,
|
|
1588
1593
|
"fallback": fallback,
|
|
1589
1594
|
}
|
|
1590
1595
|
|
|
@@ -1643,7 +1648,7 @@ def _enforce_analysis_budget(
|
|
|
1643
1648
|
return
|
|
1644
1649
|
source = budget.get("configured_source")
|
|
1645
1650
|
from sourcecode.phased_run import budget_provenance, budget_warning
|
|
1646
|
-
if not getattr(sys.stderr, "isatty", lambda: False)():
|
|
1651
|
+
if not getattr(sys.stderr, "isatty", lambda: False)() and float(configured) >= recommended:
|
|
1647
1652
|
return
|
|
1648
1653
|
|
|
1649
1654
|
if float(configured) >= recommended:
|
|
@@ -2052,6 +2057,7 @@ def _emit_command_output(
|
|
|
2052
2057
|
success_msg: "Optional[str]" = None,
|
|
2053
2058
|
stamp_envelope: bool = True,
|
|
2054
2059
|
fmt: str = "json",
|
|
2060
|
+
spill_to_temp_on_ceiling: bool = False,
|
|
2055
2061
|
) -> None:
|
|
2056
2062
|
"""Unified output pipeline: stdout | file | clipboard.
|
|
2057
2063
|
|
|
@@ -2077,12 +2083,33 @@ def _emit_command_output(
|
|
|
2077
2083
|
# to give that command a guard. `--output FILE` is exempt inside `exceeds`.
|
|
2078
2084
|
_over = _output_ceiling_error(content, to_file=output_path is not None)
|
|
2079
2085
|
if _over is not None:
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
+
if spill_to_temp_on_ceiling and output_path is None:
|
|
2087
|
+
_fd, _spill_name = tempfile.mkstemp(prefix="ask-output-", suffix=".json")
|
|
2088
|
+
os.close(_fd)
|
|
2089
|
+
_spill_path = Path(_spill_name)
|
|
2090
|
+
_spill_receipt = _safe_write_file(_spill_path, content)
|
|
2091
|
+
if _spill_receipt.get("written"):
|
|
2092
|
+
_spill_payload = {
|
|
2093
|
+
"status": "output_spilled",
|
|
2094
|
+
"output_path": str(_spill_path),
|
|
2095
|
+
"bytes": len(content.encode("utf-8")),
|
|
2096
|
+
"estimated_tokens": _output_ceiling.estimate_tokens(content),
|
|
2097
|
+
"reason": "stdout output exceeded the configured ceiling",
|
|
2098
|
+
}
|
|
2099
|
+
content = _serialize_dict(_spill_payload, fmt)
|
|
2100
|
+
_over = None
|
|
2101
|
+
else:
|
|
2102
|
+
try:
|
|
2103
|
+
_spill_path.unlink(missing_ok=True)
|
|
2104
|
+
except OSError:
|
|
2105
|
+
pass
|
|
2106
|
+
if _over is not None:
|
|
2107
|
+
_emit_error_json(
|
|
2108
|
+
_over.pop("code"),
|
|
2109
|
+
_over.pop("message"),
|
|
2110
|
+
**_over,
|
|
2111
|
+
)
|
|
2112
|
+
raise typer.Exit(1)
|
|
2086
2113
|
if output_path is not None:
|
|
2087
2114
|
_receipt = _safe_write_file(output_path, content)
|
|
2088
2115
|
_mark_output_delivered()
|
|
@@ -2296,9 +2323,30 @@ try:
|
|
|
2296
2323
|
)
|
|
2297
2324
|
return flag, hint
|
|
2298
2325
|
|
|
2326
|
+
def _nearby_command_flag_hint(exc: Any) -> Optional[str]:
|
|
2327
|
+
"""Suggest only an option that is at most two edits away."""
|
|
2328
|
+
option = str(getattr(exc, "option_name", None) or "").strip()
|
|
2329
|
+
command = getattr(getattr(exc, "ctx", None), "command", None)
|
|
2330
|
+
options = {
|
|
2331
|
+
opt
|
|
2332
|
+
for param in (getattr(command, "params", ()) or ())
|
|
2333
|
+
for opt in (getattr(param, "opts", ()) or ())
|
|
2334
|
+
if opt.startswith("--")
|
|
2335
|
+
}
|
|
2336
|
+
matches = difflib.get_close_matches(option, sorted(options), n=1, cutoff=0.8)
|
|
2337
|
+
if not matches:
|
|
2338
|
+
return None
|
|
2339
|
+
candidate = matches[0]
|
|
2340
|
+
distance = sum(a != b for a, b in zip(option, candidate)) + abs(len(option) - len(candidate))
|
|
2341
|
+
return candidate if distance <= 2 else None
|
|
2342
|
+
|
|
2299
2343
|
def _json_click_usage_error_show(self: Any, file: Any = None) -> None: # type: ignore[override]
|
|
2300
2344
|
import json as _je
|
|
2301
2345
|
_flag, _hint = _misplaced_global_flag_hint(self)
|
|
2346
|
+
if not _hint:
|
|
2347
|
+
_nearby = _nearby_command_flag_hint(self)
|
|
2348
|
+
if _nearby:
|
|
2349
|
+
_hint = f"Did you mean `{_nearby}`?"
|
|
2302
2350
|
_context: dict[str, object] = {}
|
|
2303
2351
|
if _flag:
|
|
2304
2352
|
_context["flag"] = _flag
|
|
@@ -5596,6 +5644,14 @@ def prepare_context_cmd(
|
|
|
5596
5644
|
"--base",
|
|
5597
5645
|
help="Git ref for delta task (e.g. HEAD~3, main). `--base` is an alias.",
|
|
5598
5646
|
),
|
|
5647
|
+
files: Optional[str] = typer.Option(
|
|
5648
|
+
None,
|
|
5649
|
+
"--files",
|
|
5650
|
+
help=(
|
|
5651
|
+
"(review-pr) Changed files: a text file with one path per line, "
|
|
5652
|
+
"a comma-separated list, or '-' for stdin."
|
|
5653
|
+
),
|
|
5654
|
+
),
|
|
5599
5655
|
llm_prompt: bool = typer.Option(
|
|
5600
5656
|
False,
|
|
5601
5657
|
"--llm-prompt",
|
|
@@ -5844,6 +5900,9 @@ def prepare_context_cmd(
|
|
|
5844
5900
|
pass
|
|
5845
5901
|
return
|
|
5846
5902
|
|
|
5903
|
+
_scope_files = None
|
|
5904
|
+
if task == "review-pr" and files is not None:
|
|
5905
|
+
_scope_files, _ = _resolve_files_option(files, command="review-pr")
|
|
5847
5906
|
builder = TaskContextBuilder(target)
|
|
5848
5907
|
_progress = Progress()
|
|
5849
5908
|
_phase = f"analyzing ({task})"
|
|
@@ -5899,6 +5958,7 @@ def prepare_context_cmd(
|
|
|
5899
5958
|
_result_box.append(builder.build(
|
|
5900
5959
|
task, since=since, symptom=symptom, fast=fast,
|
|
5901
5960
|
include_config=include_config, all_gaps=all_gaps,
|
|
5961
|
+
scope_files=_scope_files,
|
|
5902
5962
|
))
|
|
5903
5963
|
except BaseException as _exc: # surfaced below, never swallowed
|
|
5904
5964
|
_error_box.append(_exc)
|
|
@@ -5950,7 +6010,7 @@ def prepare_context_cmd(
|
|
|
5950
6010
|
else:
|
|
5951
6011
|
output = _result_box[0]
|
|
5952
6012
|
else:
|
|
5953
|
-
output = builder.build(task, since=since, symptom=symptom, fast=fast, include_config=include_config, all_gaps=all_gaps)
|
|
6013
|
+
output = builder.build(task, since=since, symptom=symptom, fast=fast, include_config=include_config, all_gaps=all_gaps, scope_files=_scope_files)
|
|
5954
6014
|
finally:
|
|
5955
6015
|
_progress.finish()
|
|
5956
6016
|
_t_total = (_time.perf_counter() - _t0) * 1000
|
|
@@ -6356,7 +6416,12 @@ def prepare_context_cmd(
|
|
|
6356
6416
|
# a payload document like any other. `github-comment` is Markdown and is
|
|
6357
6417
|
# passed through untouched by the conversion.
|
|
6358
6418
|
_emit_command_output(
|
|
6359
|
-
_pc_content, output_path, copy,
|
|
6419
|
+
_pc_content, output_path, copy,
|
|
6420
|
+
success_msg=(
|
|
6421
|
+
f"{task} context written to {output_path}"
|
|
6422
|
+
if output_path is not None else None
|
|
6423
|
+
),
|
|
6424
|
+
fmt=("yaml" if format == "yaml" else "json")
|
|
6360
6425
|
)
|
|
6361
6426
|
|
|
6362
6427
|
try:
|
|
@@ -8395,6 +8460,7 @@ def plan_cmd(
|
|
|
8395
8460
|
f"{data['covering_tests']['count']} covering tests, "
|
|
8396
8461
|
f"{data['affected_endpoints']['count']} endpoints)"
|
|
8397
8462
|
),
|
|
8463
|
+
spill_to_temp_on_ceiling=True,
|
|
8398
8464
|
)
|
|
8399
8465
|
|
|
8400
8466
|
# C3-29: the payload already names the resolution and lists nothing; the
|
|
@@ -8774,6 +8840,10 @@ def baseline_trend_cmd(
|
|
|
8774
8840
|
help="A repository whose history to read, or a directory of stored "
|
|
8775
8841
|
"architectural-baseline-v1 artifacts (default: current directory).",
|
|
8776
8842
|
),
|
|
8843
|
+
history_dir: Optional[Path] = typer.Option(
|
|
8844
|
+
None, "--dir", "--history-dir",
|
|
8845
|
+
help="Directory containing stored architectural-baseline-v1 artifacts.",
|
|
8846
|
+
),
|
|
8777
8847
|
output_path: Optional[Path] = typer.Option(
|
|
8778
8848
|
None, "--output", "-o",
|
|
8779
8849
|
help="Write output to a file instead of stdout.",
|
|
@@ -8793,7 +8863,7 @@ def baseline_trend_cmd(
|
|
|
8793
8863
|
ask baseline trend ./repo/.ask/baselines
|
|
8794
8864
|
"""
|
|
8795
8865
|
_enforce_format("baseline-trend", format)
|
|
8796
|
-
trend_dir = _resolve_history_dir(directory.resolve())
|
|
8866
|
+
trend_dir = _resolve_history_dir((history_dir or directory).resolve())
|
|
8797
8867
|
_require_dir(trend_dir, "trend")
|
|
8798
8868
|
|
|
8799
8869
|
from sourcecode.architectural_baseline import build_trend, load_baselines_dir
|
|
@@ -8830,6 +8900,10 @@ def trend_cmd(
|
|
|
8830
8900
|
help="A repository whose history to read, or a directory of stored "
|
|
8831
8901
|
"architectural-baseline-v1 artifacts (default: current directory).",
|
|
8832
8902
|
),
|
|
8903
|
+
history_dir: Optional[Path] = typer.Option(
|
|
8904
|
+
None, "--dir", "--history-dir",
|
|
8905
|
+
help="Directory containing stored architectural-baseline-v1 artifacts.",
|
|
8906
|
+
),
|
|
8833
8907
|
output_path: Optional[Path] = typer.Option(
|
|
8834
8908
|
None, "--output", "-o",
|
|
8835
8909
|
help="Write output to a file instead of stdout.",
|
|
@@ -8855,7 +8929,8 @@ def trend_cmd(
|
|
|
8855
8929
|
ask trend ./repo/.ask/baselines
|
|
8856
8930
|
"""
|
|
8857
8931
|
baseline_trend_cmd(
|
|
8858
|
-
directory=directory,
|
|
8932
|
+
directory=directory, history_dir=history_dir,
|
|
8933
|
+
output_path=output_path, format=format, copy=copy,
|
|
8859
8934
|
)
|
|
8860
8935
|
|
|
8861
8936
|
|
|
@@ -11257,7 +11332,11 @@ def data_exposure_cmd(
|
|
|
11257
11332
|
f"data exposure written to {output_path} "
|
|
11258
11333
|
+ (
|
|
11259
11334
|
f"({exposed} route(s) carry {summary['labels']} label(s))"
|
|
11260
|
-
if data["answered"] else
|
|
11335
|
+
if data["answered"] else (
|
|
11336
|
+
"(labels were declared but none were usable)"
|
|
11337
|
+
if data.get("declaration_state") == "declared_but_unusable"
|
|
11338
|
+
else "(no labels declared — nothing measured)"
|
|
11339
|
+
)
|
|
11261
11340
|
)
|
|
11262
11341
|
),
|
|
11263
11342
|
)
|
|
@@ -12689,6 +12768,13 @@ def review_pr_cmd(
|
|
|
12689
12768
|
"`--base` is an alias: reviewing a PR, the ref is its base branch."
|
|
12690
12769
|
),
|
|
12691
12770
|
),
|
|
12771
|
+
files: Optional[str] = typer.Option(
|
|
12772
|
+
None, "--files",
|
|
12773
|
+
help=(
|
|
12774
|
+
"Changed files: a text file with one path per line, a comma-separated "
|
|
12775
|
+
"list, or '-' for stdin."
|
|
12776
|
+
),
|
|
12777
|
+
),
|
|
12692
12778
|
output_path: Optional[Path] = typer.Option(
|
|
12693
12779
|
None, "--output", "-o",
|
|
12694
12780
|
help="Write output to a file instead of stdout.",
|
|
@@ -12731,6 +12817,7 @@ def review_pr_cmd(
|
|
|
12731
12817
|
task="review-pr",
|
|
12732
12818
|
path=path,
|
|
12733
12819
|
since=since,
|
|
12820
|
+
files=files,
|
|
12734
12821
|
format=format,
|
|
12735
12822
|
copy=copy,
|
|
12736
12823
|
output_path=output_path,
|
|
@@ -14828,6 +14915,7 @@ def cold_start_cmd(
|
|
|
14828
14915
|
success_msg=f"Saved {len(_out.encode('utf-8'))} bytes to {output_path}",
|
|
14829
14916
|
stamp_envelope=False,
|
|
14830
14917
|
fmt=format,
|
|
14918
|
+
spill_to_temp_on_ceiling=True,
|
|
14831
14919
|
)
|
|
14832
14920
|
|
|
14833
14921
|
|
sourcecode/data_exposure.py
CHANGED
|
@@ -160,15 +160,26 @@ def build_data_exposure(
|
|
|
160
160
|
from sourcecode.data_labels import CONFIG_FILENAME, CONFIG_KEY
|
|
161
161
|
from sourcecode.remedies import remedy as _remedy
|
|
162
162
|
|
|
163
|
+
declaration_state = (
|
|
164
|
+
"declared_but_unusable" if decl.declared and decl.problems
|
|
165
|
+
else "declared_without_usable_labels" if decl.declared
|
|
166
|
+
else "not_declared"
|
|
167
|
+
)
|
|
168
|
+
statement = (
|
|
169
|
+
"Data labels were declared, but none could be used. See "
|
|
170
|
+
"`declaration.problems` for the rejected declarations."
|
|
171
|
+
if declaration_state == "declared_but_unusable"
|
|
172
|
+
else "No data labels are declared, so this run measured nothing."
|
|
173
|
+
)
|
|
163
174
|
payload.update(
|
|
164
175
|
{
|
|
165
176
|
"answered": False,
|
|
166
|
-
"statement": (
|
|
167
|
-
"
|
|
168
|
-
"
|
|
169
|
-
f"field name: declare one under `{CONFIG_KEY}` in "
|
|
177
|
+
"statement": statement + (
|
|
178
|
+
" A label is a judgement about a domain and is never inferred "
|
|
179
|
+
f"from a field name: declare one under `{CONFIG_KEY}` in "
|
|
170
180
|
f"{CONFIG_FILENAME} and re-run."
|
|
171
181
|
),
|
|
182
|
+
"declaration_state": declaration_state,
|
|
172
183
|
"remedy": _remedy("no_data_labels_declared").to_dict(root),
|
|
173
184
|
"declare": {
|
|
174
185
|
"file": CONFIG_FILENAME,
|
|
@@ -199,6 +210,7 @@ def build_data_exposure(
|
|
|
199
210
|
},
|
|
200
211
|
"summary": {
|
|
201
212
|
"labels": 0,
|
|
213
|
+
"declaration_state": declaration_state,
|
|
202
214
|
"seeds": 0,
|
|
203
215
|
"endpoints_exposed": None,
|
|
204
216
|
"by_evidence": {},
|
sourcecode/manifest_cache.py
CHANGED
|
@@ -23,6 +23,7 @@ def _key(root: Path) -> str:
|
|
|
23
23
|
"pom.xml", "build.gradle", "build.gradle.kts", "package.json",
|
|
24
24
|
"package-lock.json", "pyproject.toml", "Cargo.toml", "go.mod",
|
|
25
25
|
"composer.json", "Gemfile",
|
|
26
|
+
"*.csproj", "packages.lock.json",
|
|
26
27
|
}
|
|
27
28
|
paths: set[Path] = set()
|
|
28
29
|
for name in names:
|
sourcecode/path_filters.py
CHANGED
sourcecode/phased_run.py
CHANGED
|
@@ -52,6 +52,10 @@ SCHEMA_VERSION = "partial-answer-v1"
|
|
|
52
52
|
WHY_BUDGET = "budget_exhausted"
|
|
53
53
|
WHY_TORN_DOWN = "process_ended_before_completion"
|
|
54
54
|
|
|
55
|
+
# The environment variable is process-wide, but only these commands construct
|
|
56
|
+
# a PhasedRun and can stop work at a measured boundary.
|
|
57
|
+
BUDGET_RUNNER_COMMANDS = frozenset({"spring-audit", "risk", "audit-report"})
|
|
58
|
+
|
|
55
59
|
_HOW_TO_READ = (
|
|
56
60
|
"This answer is incomplete: the phases in `phases_completed` were measured "
|
|
57
61
|
"and the ones in `phases_pending` were not. Every count here is a FLOOR over "
|
|
@@ -61,6 +65,11 @@ _HOW_TO_READ = (
|
|
|
61
65
|
)
|
|
62
66
|
|
|
63
67
|
|
|
68
|
+
def budget_scope(command: str) -> str:
|
|
69
|
+
"""Return the enforcement scope published for a command's time budget."""
|
|
70
|
+
return "phase_runner" if command in BUDGET_RUNNER_COMMANDS else "advisory_only"
|
|
71
|
+
|
|
72
|
+
|
|
64
73
|
def partials_dir(root: Path) -> Path:
|
|
65
74
|
"""Where a checkpoint lives when the caller gave no `--output` path.
|
|
66
75
|
|
sourcecode/prepare_context.py
CHANGED
|
@@ -1187,7 +1187,7 @@ class TaskContextBuilder:
|
|
|
1187
1187
|
except Exception:
|
|
1188
1188
|
return None
|
|
1189
1189
|
|
|
1190
|
-
def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None, fast: bool = False, include_config: bool = False, all_gaps: bool = False) -> TaskOutput:
|
|
1190
|
+
def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None, fast: bool = False, include_config: bool = False, all_gaps: bool = False, scope_files: Optional[list[str]] = None) -> TaskOutput:
|
|
1191
1191
|
if task_name not in TASKS:
|
|
1192
1192
|
raise ValueError(
|
|
1193
1193
|
f"Unknown task '{task_name}'. Available: {', '.join(TASKS)}"
|
|
@@ -1221,8 +1221,13 @@ class TaskContextBuilder:
|
|
|
1221
1221
|
scope_source="full_scan_fallback",
|
|
1222
1222
|
repo_root=str(self.root),
|
|
1223
1223
|
)
|
|
1224
|
-
|
|
1225
|
-
|
|
1224
|
+
if scope_files is not None:
|
|
1225
|
+
_raw_scope = scope_files
|
|
1226
|
+
_pr_scope_source = "explicit_files"
|
|
1227
|
+
_pr_committed_files, _pr_uncommitted_files = [], []
|
|
1228
|
+
else:
|
|
1229
|
+
_raw_scope, _pr_scope_source, _pr_committed_files, _pr_uncommitted_files = \
|
|
1230
|
+
self._get_pr_scope_files(since=since)
|
|
1226
1231
|
if _raw_scope is None:
|
|
1227
1232
|
# Explicit --since ref is invalid
|
|
1228
1233
|
_avail_pr, _sug_pr = self._get_available_refs(since or "")
|
sourcecode/release_info.py
CHANGED
|
@@ -27,7 +27,7 @@ from typing import Any, Optional
|
|
|
27
27
|
#: The date `__version__` was released, from that version's CHANGELOG entry.
|
|
28
28
|
#: `tests/test_release_info.py` fails the build if the two disagree, so this is
|
|
29
29
|
#: a copy of a fact rather than a second authority for it.
|
|
30
|
-
RELEASE_DATE = "2026-08-
|
|
30
|
+
RELEASE_DATE = "2026-08-19"
|
|
31
31
|
|
|
32
32
|
#: Releases per day, measured over the 30 releases before this one (the same
|
|
33
33
|
#: computation the test re-runs against the CHANGELOG). Stable across window
|
sourcecode/repository_ir.py
CHANGED
|
@@ -47,6 +47,7 @@ from sourcecode.spring_semantic import (
|
|
|
47
47
|
)
|
|
48
48
|
from sourcecode.path_filters import (
|
|
49
49
|
is_test_path as _is_test_path,
|
|
50
|
+
is_test_or_fixture_path as _is_test_or_fixture_path,
|
|
50
51
|
is_test_fixture_module_path as _is_test_fixture_module_path,
|
|
51
52
|
)
|
|
52
53
|
from sourcecode.test_sources import matches_test_naming as _matches_test_naming
|
|
@@ -7600,7 +7601,7 @@ def is_test_source_file(rel_path: str) -> bool:
|
|
|
7600
7601
|
if _is_test_path(rel):
|
|
7601
7602
|
return True
|
|
7602
7603
|
_rel_sl = "/" + rel # leading slash: the check below is prefix-safe
|
|
7603
|
-
if "/src/main/" in _rel_sl and
|
|
7604
|
+
if "/src/main/" in _rel_sl and _is_test_or_fixture_path(rel):
|
|
7604
7605
|
_prefix = _rel_sl.split("/src/main/")[0]
|
|
7605
7606
|
_prefix_parts = [p for p in _prefix.split("/") if p]
|
|
7606
7607
|
# A module is a test module if is_test_path says so OR if any module
|
sourcecode/selftest.py
CHANGED
|
@@ -32,6 +32,7 @@ from __future__ import annotations
|
|
|
32
32
|
import json
|
|
33
33
|
import os
|
|
34
34
|
import platform as _platform
|
|
35
|
+
import re
|
|
35
36
|
import shutil
|
|
36
37
|
import subprocess
|
|
37
38
|
import sys
|
|
@@ -113,6 +114,18 @@ def _result(status: str, observed: str, **extra: Any) -> "dict[str, Any]":
|
|
|
113
114
|
return {"status": status, "observed": observed, **extra}
|
|
114
115
|
|
|
115
116
|
|
|
117
|
+
def _platform_label() -> str:
|
|
118
|
+
"""Name Windows by its build, since release() is ``10`` for Windows 11 too."""
|
|
119
|
+
system = _platform.system()
|
|
120
|
+
release = _platform.release()
|
|
121
|
+
if system == "Windows":
|
|
122
|
+
candidates = [_platform.version(), *_platform.win32_ver()]
|
|
123
|
+
builds = [int(value) for text in candidates for value in re.findall(r"\d+", text)]
|
|
124
|
+
if any(build >= 22000 for build in builds):
|
|
125
|
+
release = "11"
|
|
126
|
+
return f"{system} {release}"
|
|
127
|
+
|
|
128
|
+
|
|
116
129
|
# ── the rows ────────────────────────────────────────────────────────────────
|
|
117
130
|
|
|
118
131
|
|
|
@@ -127,7 +140,9 @@ def _check_ask09(subject: Subject) -> "dict[str, Any]":
|
|
|
127
140
|
"so the guard this row is about never ran — a bigger repository is "
|
|
128
141
|
"needed to exercise it here",
|
|
129
142
|
)
|
|
130
|
-
|
|
143
|
+
error = body.get("error")
|
|
144
|
+
error_body = error if isinstance(error, dict) else body
|
|
145
|
+
hint = str(error_body.get("hint") or "")
|
|
131
146
|
basis = body.get("hint_basis") or {}
|
|
132
147
|
declared = list(basis.get("declared_flags") or [])
|
|
133
148
|
if "No smaller inline variant is available" in hint and declared:
|
|
@@ -386,7 +401,7 @@ def run_selftest(
|
|
|
386
401
|
subject = Subject(
|
|
387
402
|
repo=Path(repo),
|
|
388
403
|
entry_point=entry_point or _entry_point(),
|
|
389
|
-
platform=f"{
|
|
404
|
+
platform=f"{_platform_label()} / python "
|
|
390
405
|
f"{_platform.python_version()}",
|
|
391
406
|
)
|
|
392
407
|
try:
|
sourcecode/summarizer.py
CHANGED
|
@@ -47,6 +47,13 @@ _CODE_EXTENSIONS = {
|
|
|
47
47
|
".go", ".java", ".kt", ".rs", ".rb",
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
_EXTENSION_LANGUAGE = {
|
|
51
|
+
".py": "Python", ".js": "JavaScript", ".ts": "TypeScript",
|
|
52
|
+
".tsx": "TypeScript", ".jsx": "JavaScript", ".mjs": "JavaScript",
|
|
53
|
+
".cjs": "JavaScript", ".go": "Go", ".java": "Java", ".kt": "Kotlin",
|
|
54
|
+
".rs": "Rust", ".rb": "Ruby",
|
|
55
|
+
}
|
|
56
|
+
|
|
50
57
|
_ARCH_LAYER_PATTERNS: dict[str, dict[str, list[str]]] = {
|
|
51
58
|
"mvc": {
|
|
52
59
|
"controller": ["controllers", "routes", "views", "handlers"],
|
|
@@ -150,10 +157,20 @@ class ProjectSummarizer:
|
|
|
150
157
|
# `migrate-check.java_files_scanned` (Java handed to the scan). Three
|
|
151
158
|
# correct figures under three unqualified names read as drift, so each
|
|
152
159
|
# says which population it counted.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
160
|
+
src_extensions = {
|
|
161
|
+
Path(p).suffix.lower() for p in sm.file_paths
|
|
162
|
+
if Path(p).suffix.lower() in _CODE_EXTENSIONS
|
|
163
|
+
}
|
|
164
|
+
src_count = sum(1 for p in sm.file_paths if Path(p).suffix.lower() in src_extensions)
|
|
165
|
+
src_languages = sorted({_EXTENSION_LANGUAGE[e] for e in src_extensions})
|
|
166
|
+
if src_count > 3:
|
|
167
|
+
counted = ", ".join(src_languages) or "supported code extensions"
|
|
168
|
+
size_hint = (
|
|
169
|
+
f", {src_count} source files with supported code extensions "
|
|
170
|
+
f"({counted})"
|
|
171
|
+
)
|
|
172
|
+
else:
|
|
173
|
+
size_hint = ""
|
|
157
174
|
|
|
158
175
|
base = f"{type_label} in {stack_name}{fw_part}{arch_suffix}{size_hint}."
|
|
159
176
|
|
sourcecode/validation_surface.py
CHANGED
|
@@ -792,7 +792,15 @@ def _reconcile_with_code_axis(
|
|
|
792
792
|
|
|
793
793
|
summary = result.setdefault("summary", {})
|
|
794
794
|
summary["body_endpoints_in_code"] = body_in_code
|
|
795
|
-
|
|
795
|
+
declared_routes = summary.get("endpoints_with_body", 0)
|
|
796
|
+
# BC-002: keep the historical key during its compatibility window, while
|
|
797
|
+
# giving new consumers a name that states the population literally.
|
|
798
|
+
summary["endpoints_with_declared_constraints"] = declared_routes
|
|
799
|
+
summary["declared_constraint_routes"] = declared_routes
|
|
800
|
+
summary["endpoints_with_body_basis"] = (
|
|
801
|
+
"legacy alias for routes with a declared constraint surface (OpenAPI or DTO); "
|
|
802
|
+
"use endpoints_with_declared_constraints for the unambiguous name."
|
|
803
|
+
)
|
|
796
804
|
summary["axis_note"] = (
|
|
797
805
|
"endpoints_with_body counts routes with a DECLARED constraint surface "
|
|
798
806
|
"(OpenAPI or DTO); body_endpoints_in_code counts body-carrying handlers "
|
sourcecode/verify_edit.py
CHANGED
|
@@ -243,7 +243,12 @@ def _can_move_a_verdict(rel_path: str) -> bool:
|
|
|
243
243
|
extension nobody has classified — is analysed.
|
|
244
244
|
"""
|
|
245
245
|
parts = [p for p in rel_path.split("/") if p]
|
|
246
|
-
|
|
246
|
+
# Reuse the shared tool/editor-state authority. A local list here let
|
|
247
|
+
# `.claude/settings.json` force a full double build even though no analyzer
|
|
248
|
+
# can read it (B4).
|
|
249
|
+
from sourcecode.path_filters import analysis_reads
|
|
250
|
+
|
|
251
|
+
if not analysis_reads(rel_path):
|
|
247
252
|
return False
|
|
248
253
|
try:
|
|
249
254
|
from sourcecode.repository_ir import PRUNED_TREE_DIRS
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 5.8.
|
|
3
|
+
Version: 5.8.8
|
|
4
4
|
Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/HarounDominique/sourcecode
|
|
6
6
|
Project-URL: Repository, https://github.com/HarounDominique/sourcecode
|
|
@@ -49,7 +49,7 @@ Description-Content-Type: text/markdown
|
|
|
49
49
|
|
|
50
50
|
**Context · Impact · Migration · Architecture · Review — everything from one structural model.**
|
|
51
51
|
|
|
52
|
-

|
|
53
53
|

|
|
54
54
|
|
|
55
55
|
Copyright (c) 2026 Dominique Haroun. All rights reserved. ASK Engine 5.7.0 and
|
|
@@ -139,7 +139,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
139
139
|
# pip / pipx
|
|
140
140
|
pipx install sourcecode # or: pip install sourcecode
|
|
141
141
|
|
|
142
|
-
ask version # ask 5.8.
|
|
142
|
+
ask version # ask 5.8.8 — and, on a build that has aged,
|
|
143
143
|
# how many releases have probably shipped since
|
|
144
144
|
```
|
|
145
145
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=yZjHsrmtVA5g1MKeZTpLxBVjvxCMkraqyf_rqnp0jpE,308
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=yJBKjNpkY6bpueYJ2YnRezen3sYZDecEt7WaaNWdqug,9466
|
|
3
3
|
sourcecode/answer_bounds.py,sha256=_51_DCuYEUmI_xwUFxDMiprSavkNakkLiIBbPjevQ3M,9793
|
|
4
4
|
sourcecode/archetype.py,sha256=uubguXP5ltQt25ACNVm9kX4KRTe7JCqkFfg4H9zckCo,40971
|
|
@@ -25,7 +25,7 @@ sourcecode/chain_rules.py,sha256=Bi6UHfgd-GxWswmnHRcPz5jdbAuqka3Zkz_P-MTvqhw,127
|
|
|
25
25
|
sourcecode/change_plan.py,sha256=FAbLtexNThXp51STU-F2a_3AQd1LWd3NsKWMnMNJrPo,12333
|
|
26
26
|
sourcecode/cir_graphs.py,sha256=RytqSv1WF8kdlfXj2fPQz16LY4akS2kyThbdWL8beCQ,14835
|
|
27
27
|
sourcecode/classifier.py,sha256=JBzPwSSrDG-tUHAbcKB678HRbjLpD-ohzbzzO62mgpo,20114
|
|
28
|
-
sourcecode/cli.py,sha256=
|
|
28
|
+
sourcecode/cli.py,sha256=FW8siR2aWLMR5Db81zH4N_pVGOqfPoi5RS_IOrWgK1s,723704
|
|
29
29
|
sourcecode/client_calls.py,sha256=YOPnfRZzRyqmdHvXSVeiunW8cuiuCuWXacNIQRgKMCw,22640
|
|
30
30
|
sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
|
|
31
31
|
sourcecode/compare.py,sha256=2GdDy0qkDSgmkkpgvoCHitoLj3mt30EeFgJ8PGEXJng,14712
|
|
@@ -42,7 +42,7 @@ sourcecode/contract_init.py,sha256=bXRCqWM9tvrDLeT8j7ea6IWf87jB4VJxnlSzGBsRRXo,1
|
|
|
42
42
|
sourcecode/contract_model.py,sha256=nRxJKPMs1VHwFTa8AVXhGmaLjti3Lr2sjHDpWgv1bfE,3917
|
|
43
43
|
sourcecode/contract_pipeline.py,sha256=4XE-xhab7ysdExXyUOBl2hAH4MABo7moKnT9V4XcEwQ,30243
|
|
44
44
|
sourcecode/coverage_parser.py,sha256=X9scuUWxacahYTrTBGea2Ku-sf-WCVcnc9OEB35_3IU,19340
|
|
45
|
-
sourcecode/data_exposure.py,sha256=
|
|
45
|
+
sourcecode/data_exposure.py,sha256=8yhlPhLLQt1gcnFfsFO2OFOdDdPkSbzElugrB_nsIJ8,17706
|
|
46
46
|
sourcecode/data_labels.py,sha256=c_hmgudTE68cVRdywfbfonr7nUWIL-JH2HOXB8Pplv0,8453
|
|
47
47
|
sourcecode/declarations.py,sha256=Jr7oJDg004sFw0TvJySNVggXFT2jB6hQgbeQK2qreVo,7000
|
|
48
48
|
sourcecode/defect_identity.py,sha256=XS2VFDImg9NLaRImybKTeyk-CoDSsXmchldg0dwvM7c,7028
|
|
@@ -82,7 +82,7 @@ sourcecode/identity_fallback.py,sha256=uRC_j_80uaI4Ue4tf-OhKX4h472dmufYcjEFGSxY2
|
|
|
82
82
|
sourcecode/integration_coordinates.py,sha256=rvuApnS7WdRUbeQLg_oKj-Gi0koO2RKdw6HenXNgrDo,12443
|
|
83
83
|
sourcecode/jdk_exports.py,sha256=fCrlwNAXUT9gge_joq6kMnY3zJxYB2pxqy-0w3o3MJI,874
|
|
84
84
|
sourcecode/license.py,sha256=vvgE1vBUWh8ozWI7USYuteh6noxntnjuzDQIPF-l4rA,27795
|
|
85
|
-
sourcecode/manifest_cache.py,sha256=
|
|
85
|
+
sourcecode/manifest_cache.py,sha256=HR0wPIFA5odNGzHrMHS_H2GdveU6ZnyBeoGThB0BLoY,2200
|
|
86
86
|
sourcecode/mcp_nudge.py,sha256=lKemOqK_wny2u7Ymcr2Idi5Kx8pXY02jCi-_nJYLGMg,2992
|
|
87
87
|
sourcecode/metrics_analyzer.py,sha256=gLoRWKygF18jLgwsqmGXSWopw-f5iO1VbM7Jjdif6ag,22809
|
|
88
88
|
sourcecode/migrate_check.py,sha256=fI77EFwAfWV8cSzM7Uqr9GoO2VhjIVaaM_tBItSt598,152697
|
|
@@ -99,14 +99,14 @@ sourcecode/parallel.py,sha256=QocwdZoUabdxzd_jEz7f6B2XMsYWFaCFaIl0tIEQs3g,10546
|
|
|
99
99
|
sourcecode/parse_cache.py,sha256=cVYF2ZSC9F5RCRp4UBuAyMG_u_Tm0ZG78gBzMrER1Sg,36772
|
|
100
100
|
sourcecode/partial_contract.py,sha256=x_N_0bF3mpNNjXd7JFB1zr6LSIroo2zN5GCz14GXvXQ,3510
|
|
101
101
|
sourcecode/path_admission.py,sha256=OGNSoluhVh8YYOBZBnACUrkmH4Tp_yxjGLifkcsxQpo,5838
|
|
102
|
-
sourcecode/path_filters.py,sha256=
|
|
102
|
+
sourcecode/path_filters.py,sha256=y2SmiExrKQMn-FUGgSelqo5Gr0SimDSQBwwnqtMcy3U,12491
|
|
103
103
|
sourcecode/perf.py,sha256=2Rz0QdDI7KeMRXsuepFJYVBtACdZLTP-TVe1dAcVvDg,60446
|
|
104
|
-
sourcecode/phased_run.py,sha256=
|
|
104
|
+
sourcecode/phased_run.py,sha256=fZxxiCX-CykagFVR_Fp2Tin3H6FDSV8YRpYGtsRAXj8,18356
|
|
105
105
|
sourcecode/pipe_contract.py,sha256=PML0Er5d8uDyec4OrdUXuyqHbGPUYndjeaKUjkFz2u4,8369
|
|
106
106
|
sourcecode/posture.py,sha256=m1sYDLtGvVkvDEVqMxuDvAKz4-KPj0z5Q6x3V_5U-FM,83276
|
|
107
107
|
sourcecode/pr_comment_renderer.py,sha256=239PmJdf95av_ZW236C7_tvq_ahECeuF9AZxrheJ4OQ,15573
|
|
108
108
|
sourcecode/pr_impact.py,sha256=JRVKV0LiVpE0aTy_Wcy-o0EhWdd1OChxHRIcS0pZHl4,36216
|
|
109
|
-
sourcecode/prepare_context.py,sha256
|
|
109
|
+
sourcecode/prepare_context.py,sha256=-FFvr8NgCAHckTp93ZjWEdI3fFURaLkTZiwt3_0eqgg,239532
|
|
110
110
|
sourcecode/product_info.py,sha256=0zjZEutL7iDdp9aVyIumIrNyNn9VWYFc2lIytSbjBP4,727
|
|
111
111
|
sourcecode/progress.py,sha256=naKbWFqf-eTE3vtLsi-eHYILz0p3pxCASAzMmvxUIAI,21643
|
|
112
112
|
sourcecode/provenance.py,sha256=gLbsLv0gze7k7ChM4i5M3MwKfFPzs8wzM72rUd2C5sM,7342
|
|
@@ -117,13 +117,13 @@ sourcecode/reconciliation.py,sha256=pmqc0JNcAagKGWDcjH2rLDlKlAbfJCVUj86vV--YEXQ,
|
|
|
117
117
|
sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
|
|
118
118
|
sourcecode/reference_facts.py,sha256=mx7H-Ax5y8sKAQHrnJCXlgjkUzqZtQTByv9Zy2lxKQQ,15500
|
|
119
119
|
sourcecode/regress.py,sha256=aXN6n7t39OOSOUvUs4HTH7UGVwl-tzhjXjHfDXNtp04,11162
|
|
120
|
-
sourcecode/release_info.py,sha256=
|
|
120
|
+
sourcecode/release_info.py,sha256=EtmaGDnLZlbqvjMB0i4UAjGLbKage5rPvQetuCHR20w,6064
|
|
121
121
|
sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
|
|
122
122
|
sourcecode/remediation_diff.py,sha256=fGYqPmQF7bO54L8ivztX5PgmW4fVu54gNcG6OEQ192U,9730
|
|
123
123
|
sourcecode/remedies.py,sha256=V2_cD4pLq8bKRkzF-3RGFoiu1xWYZS384WDVp1MrlZw,10225
|
|
124
124
|
sourcecode/rename_refactor.py,sha256=h6dNFlB9aZ_3q6heeHBkgXQeXaT03nvPSsYH6P8qxFg,12965
|
|
125
125
|
sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
|
|
126
|
-
sourcecode/repository_ir.py,sha256
|
|
126
|
+
sourcecode/repository_ir.py,sha256=-tV6QuCu56kUaosz4-SgkPdL46DuOlvlRAYxMOhtvXU,486494
|
|
127
127
|
sourcecode/ris.py,sha256=zlns4esv1tK8VDIYzOzRmw5yM6VLx7oxaPNB_kN3it4,28617
|
|
128
128
|
sourcecode/risk.py,sha256=hXddUjiI-SS_yQ0gZyhufs4lzKwWySOKY4Lj96gL9_A,89733
|
|
129
129
|
sourcecode/risk_model.py,sha256=RoC-G15qZP5BLEwwmcr4eJ0APgvIbh1WTqhTVT-5YFE,10895
|
|
@@ -140,7 +140,7 @@ sourcecode/security_chain.py,sha256=1YZVeFLwy65kntMBhMNvz1cUpOoKCmvEqYICgxs66Gw,
|
|
|
140
140
|
sourcecode/security_config.py,sha256=LUppRDYQK0oKM4qdEOLObaeU3frFTS-paEZo7dMgtY8,5467
|
|
141
141
|
sourcecode/security_config_scan.py,sha256=iXDeulgdbAwUFR01p9PgrJED2vd4HE2FCZgqrxX5D0E,32824
|
|
142
142
|
sourcecode/security_posture.py,sha256=rVCu5-InQshwI0bWqanxg5rg_fhV3o8d35ifQeQEM7M,55197
|
|
143
|
-
sourcecode/selftest.py,sha256=
|
|
143
|
+
sourcecode/selftest.py,sha256=mGvShPbOwNqayTJ8GnpTGsscMIh5gMSItLQvDiSmb2w,17999
|
|
144
144
|
sourcecode/semantic_analyzer.py,sha256=xGwQvhU9xeOlZ4N-TPXFZT8HBHx4Rg0nPwu8nDFl00Y,95621
|
|
145
145
|
sourcecode/semantic_impact_engine.py,sha256=t09IirGC3JjQDy33JZd1_WKzQVKXkoNl3-XEUr5kjis,20563
|
|
146
146
|
sourcecode/semantic_integration_engine.py,sha256=ZjyKJKOj-adScWlYBuWx8Ee_P0F4_OImUJVnCYgopuU,20952
|
|
@@ -158,7 +158,7 @@ sourcecode/spring_security_audit.py,sha256=sn0FtdcBKbRFPZj6mZ3CMHbpGoqcj2Md9MlOt
|
|
|
158
158
|
sourcecode/spring_semantic.py,sha256=UrmLg_4gmBVmMqvSnUMgSReVEERVGv3X7LYDBEs6k_o,16598
|
|
159
159
|
sourcecode/spring_tx_analyzer.py,sha256=WBibPgkjQm33kFgERx6zLtp87FpJNE5V_4JI3xZ5zD4,50094
|
|
160
160
|
sourcecode/sql_taint.py,sha256=As1DbEdjXMBFjCTXU5ojksOzuKkP6LmnuMVnzzmckzc,26998
|
|
161
|
-
sourcecode/summarizer.py,sha256=
|
|
161
|
+
sourcecode/summarizer.py,sha256=jW68aAQ5FTVtBYN2_5GisYMFyFBCrhEaEgeaRK5ol_Y,28172
|
|
162
162
|
sourcecode/target_admission.py,sha256=wFZ4pzlxhiF6Q6s2lEAZEzcCfj1Y6xNvujjt8MdO0Qo,7154
|
|
163
163
|
sourcecode/test_gap_ranking.py,sha256=hl-tTyQUXZGJycG1b6npbLeE_DaVHaOsvPwG9qqC5y8,15711
|
|
164
164
|
sourcecode/test_sources.py,sha256=KM88qbMfLDmUV5X2nA83LjYz8A2ipj4A_2u2FhZx8gc,9440
|
|
@@ -169,8 +169,8 @@ sourcecode/token_estimate.py,sha256=G-ivzIJfVq5TLQN04dC0WK4LZJjWVYL54Ebcxe4fJ7M,
|
|
|
169
169
|
sourcecode/tree_utils.py,sha256=8GAkIfQAsvtEudIeW1l4ooH_oRtrWR8cpJQJsEa_Pfw,2093
|
|
170
170
|
sourcecode/type_usage_surface.py,sha256=51IrKRQoIoRnlsiDjHnqpJBn2rc6E59aRhgS0HTzAF0,4428
|
|
171
171
|
sourcecode/validation_inference.py,sha256=-oWJqE6PqkqcZbCFDJeWCQHgI9Dbuv5ggJWD3LE_2IU,20817
|
|
172
|
-
sourcecode/validation_surface.py,sha256=
|
|
173
|
-
sourcecode/verify_edit.py,sha256=
|
|
172
|
+
sourcecode/validation_surface.py,sha256=9hgQ4TmMHOhFatEK9Jn0eAsVMfUq83qAhB2QmjJEv9U,35861
|
|
173
|
+
sourcecode/verify_edit.py,sha256=u2UeJ4n0zDzJ9gkq-1MiLv7Msfm8mLdUy0O_ZkaKxOE,51161
|
|
174
174
|
sourcecode/verify_repo.py,sha256=aHlNIUfoVVUxTXTNtSFryP4dOFBOvCfdyC31fFRXU6s,15346
|
|
175
175
|
sourcecode/verify_rules.py,sha256=R3l6nLT6WQzUNBzxtmnxyGZcKj9qMDW38Y0c0gNdCGk,19973
|
|
176
176
|
sourcecode/version_check.py,sha256=CHp6ZxTIfo8kyHPCBgJA1uFC0xQCoXMuuOfrW8QTL8o,4942
|
|
@@ -240,13 +240,13 @@ sourcecode/telemetry/consent.py,sha256=pQdl-QeLl6Gcibn0eWHSKZrm-HYSsjpVqOnjrgFp8
|
|
|
240
240
|
sourcecode/telemetry/events.py,sha256=4_yeO58U-Cwc1Qb27VB0_EjhmroY0k91n3_VGxeALB8,2776
|
|
241
241
|
sourcecode/telemetry/filters.py,sha256=RzxauTz8HliO4BllQnXEXc7zTeqdCZi5MgqGEDuW7OQ,6570
|
|
242
242
|
sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
|
|
243
|
-
sourcecode/_docs/DEFECT-LEDGER.md,sha256=
|
|
243
|
+
sourcecode/_docs/DEFECT-LEDGER.md,sha256=0XJFc2Eve4ht3IwZFqAbd4ct34uv4vB-PC7d8NZ6nv8,728714
|
|
244
244
|
sourcecode/_docs/LICENSE,sha256=23-fk61oSWGVmkWyka-FIM0rhAPM2hcYHnsaridL8Uo,1859
|
|
245
245
|
sourcecode/_docs/NOTICE,sha256=Br-AA8Y3iJo1WXa5uP8QBpzqI5nDu3iGLbFyvDt3BOs,321
|
|
246
|
-
sourcecode/_docs/USER_GUIDE.md,sha256=
|
|
247
|
-
sourcecode-5.8.
|
|
248
|
-
sourcecode-5.8.
|
|
249
|
-
sourcecode-5.8.
|
|
250
|
-
sourcecode-5.8.
|
|
251
|
-
sourcecode-5.8.
|
|
252
|
-
sourcecode-5.8.
|
|
246
|
+
sourcecode/_docs/USER_GUIDE.md,sha256=Sk3P7oOjsYF6YxhKaxIUsM8Y0Dm1pQqrpOYXD2dUnqg,86739
|
|
247
|
+
sourcecode-5.8.8.dist-info/METADATA,sha256=vQdqwv4iB9WEAQyW1ihkDlzKeIlg8gvWVKNyqC-NTKY,50807
|
|
248
|
+
sourcecode-5.8.8.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
249
|
+
sourcecode-5.8.8.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
|
|
250
|
+
sourcecode-5.8.8.dist-info/licenses/LICENSE,sha256=23-fk61oSWGVmkWyka-FIM0rhAPM2hcYHnsaridL8Uo,1859
|
|
251
|
+
sourcecode-5.8.8.dist-info/licenses/NOTICE,sha256=Br-AA8Y3iJo1WXa5uP8QBpzqI5nDu3iGLbFyvDt3BOs,321
|
|
252
|
+
sourcecode-5.8.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|