sourcecode 5.8.14__py3-none-any.whl → 5.8.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sourcecode might be problematic. Click here for more details.

sourcecode/__init__.py CHANGED
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "5.8.14"
7
+ __version__ = "5.8.16"
@@ -16,16 +16,87 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
16
16
 
17
17
  ## Current Synchronization
18
18
 
19
- **As of 2026-08-20, release `5.8.14` and the fourth external re-audit:** the audited
20
- snapshot remains `3dde0376`; the report compares the four audit rounds and scores this one
21
- **90/100** (75, 80, 78, 90). It exercised 53/61 invocables (87%), ran no application and
22
- left the repository unchanged. Its 40+ longitudinal functional anchors are unchanged, so
23
- there is no reported product regression. The `5.8.14` release ships the implementations
24
- recorded below; they remain distinguished from facts verified on the `5.8.13` subject.
25
- Only this table declares present state.
19
+ **Latest attached audit of release `5.8.15`:** score **90/100**, coverage **54/62
20
+ invocables (87%)**, byte-identical CIR across six versions, and no longitudinal functional
21
+ regression. A new P1 affects dirty-tree `verify-edit`; BUG-1 is reproduced on the
22
+ Windows/NTFS audit host. The historical `risk` P0 remains open because nine successful
23
+ runs do not disprove two earlier exit-0/no-output failures. Only the sixth-pass section
24
+ below declares present state; older audit rounds remain for traceability.
26
25
 
27
26
  **Release status:** entries marked “pending `5.8.14`” in their historical wording are
28
- shipped in `5.8.14`; a re-audit is still required wherever the row says so.
27
+ shipped in `5.8.14`; the correction battery fixes listed above are included in `5.8.15`.
28
+
29
+ **Current release:** `5.8.16` includes two post-audit corrections: compact cold-start
30
+ identity is preserved for `no_ris`, and manifest-cache keys include Python requirements
31
+ files. The testing-repository hygiene exercise changed no external repository; it
32
+ completed 108/108 read-only workflow runs with valid JSON and exit code 0.
33
+
34
+ The battery also closed the following reproducible contract defects in `5.8.15`:
35
+ `migrate-apply` is registered across help, progress, path-admission, output-purity,
36
+ cache and schema authorities (`1d00ba2`); endpoint census output distinguishes excluded
37
+ test modules from retained test-fixture routes (`f65336d`, `014dec7`, `1c6cd0b`);
38
+ and compact posture output retains counts while omitting unresolved bodies (`dad5dad`).
39
+
40
+ The sixth audit supersedes the previous queue where it has stronger evidence: BUG-3 is
41
+ closed in stable-cache regime, BUG-1 is reproduced on the audit host, and BUG-4a is
42
+ partial because `onboard` still has no machine-readable population block.
43
+
44
+ ### Sixth Audit Pass: `5.8.15` / `saint-server` / 2026-08-20
45
+
46
+ | ID | Severity | Current status | Required direction |
47
+ |---|---|---|---|
48
+ | `BUG-6` / `AUD-588-F02` | P1 | **implemented in working tree** (`26b4ab0`), external clone integration pending: removed security findings now count as changes and both-direction unit coverage passes. | Prove the real `authenticated() -> permitAll()` clone case before release; access opening remains the gate. |
49
+ | `BUG-1` / `AUD-511-R02`, `AUD-589-B02`, `AUD-513-N06` | P1 | **reproduced on Windows/NTFS**: `spring-audit` x1.66, `modernize` x1.67, `migrate-check` x1.90, `cache warm` x2.82 versus 5.8.13; output remains byte-identical. | Profile the target host and isolate the shared slow path; add a cross-host performance guard. |
50
+ | `BUG-3` / `443b345` | P1 | **closed in stable-cache regime**: `data-exposure` is 15.6s median, 7.2s best versus 41–44s historically, with identical output. | Preserve shared-CIR reuse and record host/cache provenance. |
51
+ | `BUG-4a` / `24b4185` | P2 | **implemented in working tree** (`89ee4a5`): the RIS fast path now publishes population/unit/total like the full path. | Validate and reconcile the 6939 versus 6947 scopes/bases before release. |
52
+ | `BUG-2` / `b5acc63` | P0 | **open, hardened**: 4/4 current runs delivered artifacts; historical failures remain unrefuted. | Keep the real subprocess invariant and reproduce historical failure conditions before closure. |
53
+ | `BUG-5` / `AUD-588-B12`, `AUD-588-F03` | P2 | **open**: cost anchors remain stale; `risk` model 77.2s versus ~305s observed, warm 166.7s versus 316s. | Rebuild anchors with host, cache state, version and sample provenance. |
54
+ | `AUD-588-F05` | Validation gap | **partially covered**: dirty-tree `verify-edit` is covered; seven mutating/denied invocables remain. | Finish the clone-based matrix without modifying the original repository. |
55
+
56
+ ### Fifth Audit Pass: `5.8.14` / `saint-server` / 2026-08-20
57
+
58
+ | ID | Severity | Current status | Required direction |
59
+ |---|---|---|---|
60
+ | `BUG-1` / `AUD-511-R02`, `AUD-589-B02`, `AUD-513-N06` | P1 | **not reproduced locally; external closure pending**: isolated/interleaved controls did not reproduce the reported slowdown. | Repeat the six-run protocol on the audit host and compare 5.8.13/5.8.15 with byte-equivalent output. |
61
+ | `BUG-2` / `AUD-588-B01`, `AUD-589-B01`, `C3-122` | P0 | **open, not reproduced in five runs**: all five current `risk` runs produced exit 0 and a ~311 KB artifact; historical failures remain material. | Keep the real-entry-point output invariant. Treat `b5acc63` and the existing working-set/timing instrumentation as hardening, not field closure. |
62
+ | `BUG-3` / `AUD-511-R02`, `AUD-589-B02`, `AUD-513-N06` | P1 | **implemented in 5.8.15; numeric external remeasurement pending**: labelled runs now reuse shared CIR (`443b345`). | Re-run the controlled external benchmark; do not infer a speedup from a different host. |
63
+ | `BUG-4a` / `AUD-588-B11` residual | P2 | **closed in 5.8.15** (`24b4185`): `onboard` and `archetype` publish explicit source-file population units and totals. | Keep parity tests on both payloads. |
64
+ | `BUG-4b` / `AUD-588-B11` residual | P2 | **closed in 5.8.15** (`0376350`): help, tier table and docs agree that `risk` is supported. | Keep the single `COMMAND_TIERS` authority. |
65
+ | `BUG-4c` / `SEC-009` calibration | P2 / product decision | **by-design in 5.8.15** (`fb7632a`): medium is retained and pinned; active/profile evidence supplies context. | Reopen only with a new product-risk decision, not from the same observation. |
66
+ | `BUG-5` / `AUD-588-B12`, `AUD-588-F03` | P2 | **open, measurement improved but not portable yet**: local six-run `risk` converged at 6.56s (clean control, Broadleaf, 5.8.14); this is not comparable to the historical Windows anchor. | Add a provenance-bearing, cross-host anchor before changing the operational model. |
67
+ | `AUD-588-F05` | Validation gap | **locally verified only**: dirty-tree `verify-edit` and eight mutating/denied invocables remain unexercised externally. | Clone the repository into scratch space, edit the clone and run the dirty-tree protocol; do not use an archive without `.git`. |
68
+
69
+ **Fifth-pass anchor correction:** `posture.summary` now reports the Spring IoC population
70
+ (`population_total: 1904`, `conditional: 5`, `active: 3`, `inactive: 2`, `unconditional:
71
+ 1899`) instead of the former type count (`6667`). This is an intentional correction with
72
+ explicit units, not a regression. The audit remains static: it did not start the application.
73
+
74
+ **Recommended order:** BUG-1 measurement/isolation → `risk` phase-cost investigation →
75
+ BUG-3 reuse comparison → BUG-4 contract cleanup → BUG-5 calibration. Keep EclipseLink
76
+ automation deferred; provider non-coverage is the honest current behavior. `F02` remains
77
+ partial, `F03` open, `F04` explicit opt-in and `F05` locally verified only.
78
+
79
+ ### Historical Execution Queue: `5.8.14` Intake
80
+
81
+ | ID | Severity | Current status | Required direction |
82
+ |---|---|---|---|
83
+ | `R-1` | High | **closed by `424fb1c`** | Delivery timing now publishes current elapsed time plus cache and phase provenance. |
84
+ | `R-2` | Medium | **closed by `5158b92`** | `BC-004` declares removed fields, replacements and effective version. |
85
+ | `R-3` | Medium | **closed by `88aec04`** | Global cache purge now clears parse and Shared CIR stores. |
86
+ | `R-4` | Medium | **closed by `04fd04f`** | Payload layers are homonymous with their authorities: `cir`, `parse_store`, `snapshot`, `ris`. |
87
+ | `N-6` | Medium | **not reproduced after cache fixes** | Isolated cold `openmrs-core`: 5.18 s wall / 4.76 s internal, all four layers cold. Retain the field report as an environment-specific reproduction request. |
88
+ | `B-3` | Medium, core/supported | **closed by `773268d`** | Help no longer claims maximum signal; `sibling_view` points to compact. |
89
+ | `B-6` | Medium, core | **closed by `17c6e06`** | Partial answers publish `overrun_bound` and its wall-clock basis. |
90
+ | `B-8` | Low | **closed by `52593b6`** | Compact posture retains one unresolved sample when the total is non-zero. |
91
+ | `N-5` | Low, core | **closed by `c68083f`** | Progress and payload name the Java population. |
92
+ | `N-7` | Low, core | **closed by `082e9de`** | Bounded fuzzy candidates and matching message hints handle close typos. |
93
+
94
+ **Recommended features/hardening:** response timing provenance and root cache-layer
95
+ disclosure; a single cache reset/state authority; compact/agent parity or sibling-view
96
+ metadata; a partial-budget exit contract; shared population/units authority; reusable fuzzy
97
+ symbol suggestions; and a release gate for `breaking-changes-v1` whenever core or supported
98
+ fields are removed. These extend `AUD-588-F02` and `AUD-588-F03`. `AUD-588-F01` EclipseLink
99
+ automation remains deferred; provider non-coverage is the current product behavior.
29
100
 
30
101
  | Rows | Current status | Evidence |
31
102
  |---|---|---|
@@ -6,7 +6,11 @@
6
6
 
7
7
  ## What is it
8
8
 
9
- A local CLI. No API keys. No network calls. No account required.
9
+ A local CLI. Source analysis runs on your machine; it does not need an API key or
10
+ an account. Anonymous telemetry is enabled by default and may send operational
11
+ event metadata, never source content, paths, secrets, or command output. Disable
12
+ it with `ask telemetry disable`, `SOURCECODE_TELEMETRY=0`, or `DO_NOT_TRACK=1`
13
+ before a run when policy requires it. See [privacy.md](privacy.md).
10
14
 
11
15
  It builds a deterministic symbol graph (classes, annotations, injection edges, HTTP routes) from your repository's source files and answers structural questions about that graph. All analysis is static — it reads code, not runtime behavior.
12
16
 
@@ -42,7 +46,7 @@ CLI commands — impact, endpoints, spring-audit, explain, … each a pro
42
46
  The key idea: the extraction is **content-addressed**. Commands reuse the parse cache and,
43
47
  where their analysed scope matches, the shared Canonical IR; `ask cache model` names what a
44
48
  warm buys for each command rather than implying that every projection costs the same. In
45
- 5.8.12, `validation` enters through that shared CIR and `data-exposure` reuses one semantic
49
+ 5.8.16, `validation` enters through that shared CIR and `data-exposure` reuses one semantic
46
50
  model across all declared label seeds. (The extraction and consumption contract is fixed in
47
51
  the architecture ADRs 0001–0004 under `docs/architecture/`.)
48
52
 
@@ -138,16 +142,20 @@ pipx install sourcecode # isolated install, no venv needed
138
142
 
139
143
  # Verify
140
144
  ask version
141
- # ask 5.8.14
145
+ # ask 5.8.16
142
146
  ```
143
147
 
144
148
  Requires Python 3.9+.
145
149
 
146
150
  ---
147
151
 
148
- ## License activation (Pro features)
152
+ ## Entitlement and activation
149
153
 
150
- Free features work immediately after install. Pro commands (`impact` full output, `review-pr`, `fix-bug`, `modernize`) require an active license. `spring-audit`, `impact-chain`, `endpoints`, `onboard`, `repo-ir`, and `cold-start` are free.
154
+ Base analysis works immediately after install. The effective entitlement is a runtime
155
+ fact, not a command list in this document: use `ask auth status` to see what runs,
156
+ why it runs, and when that source changes. During the current early-adoption period
157
+ the Pro entitlement may be unlocked without a key; an activated key remains the
158
+ explicit way to establish an entitlement when one is required.
151
159
 
152
160
  ```bash
153
161
  ask activate SC-XXXX-XXXX-XXXX
@@ -179,7 +187,7 @@ estimate scaled by file count would be wrong in the direction that costs you a s
179
187
  and an anchor printed without its release goes on recommending a nightly job for a command
180
188
  that has come to finish in seconds (C3-97).
181
189
 
182
- Forty commands and six command groups exist. Four of them carry most of the measured
190
+ 41 commands and six command groups exist. Four of them carry most of the measured
183
191
  value in field use, and they are the ones to learn first:
184
192
 
185
193
  | Start with | Because |
@@ -256,6 +264,9 @@ ask onboard . --output onboard.json
256
264
  ```
257
265
 
258
266
  Output: architecture summary, subsystems, key entry points, hotspots, tech-debt signals, analysis gaps.
267
+ The payload also publishes `population`, `population_unit` and `population_total`; these
268
+ describe the non-test source-file universe behind the onboarding ranking, not every file
269
+ in the repository.
259
270
 
260
271
  ### `ask endpoints` *(core)*
261
272
 
@@ -342,6 +353,7 @@ ask spring-audit . --output audit.json
342
353
  ask spring-audit . --since origin/main --fail-on-new # gate on what THIS change added
343
354
  ask spring-audit . --remediation-diff # the patches a reviewer applies
344
355
  ask spring-audit . --clusters # the work items behind the findings
356
+ ask spring-audit . --ci --format github-actions # native CI annotation plus ASK_CI summary
345
357
  ```
346
358
 
347
359
  **`--since <ref>` / `--fail-on-new` — the gate a repository with debt can turn on.**
@@ -379,7 +391,7 @@ disabled changes runtime behaviour nobody measured; a diff is how a person makes
379
391
  call with the evidence in front of them.
380
392
 
381
393
  **TX patterns (TX-001..TX-006):** proxy bypass, nested transactions, readOnly propagation, NOT_SUPPORTED in active TX, exception swallowing, self-invocation of a `@Transactional` sibling.
382
- **SEC patterns (SEC-001..SEC-008):** unsecured endpoints, CVE-2025-41248 `@PreAuthorize` inheritance bypass, `@Transactional` on controllers, passwords stored under a fast unsalted digest, CSRF disabled under session-bearing authentication, cookies created without the Secure attribute, credentials stored in a deployment descriptor, and SQL built by MyBatis string interpolation. **DEAD-001** reports a security control that is present only in comments and therefore disabled.
394
+ **SEC patterns (SEC-001..SEC-009):** unsecured endpoints, CVE-2025-41248 `@PreAuthorize` inheritance bypass, `@Transactional` on controllers, passwords stored under a fast unsalted digest, CSRF disabled under session-bearing authentication, cookies created without the Secure attribute, credentials stored in a deployment descriptor, SQL built by MyBatis string interpolation, and anonymous request-chain exceptions. **DEAD-001** reports a security control that is present only in comments and therefore disabled.
383
395
  **GATE patterns (GATE-001..GATE-004):** custom AOP/security-gate risks over the same custom-gate authority published in `security_posture`: proxy bypass (`private`/`final`/self-invocation), advice that appears to fail open, disabled or tautological annotation attributes, and fragile `args[0]`/reflection binding.
384
396
 
385
397
  Each finding includes `severity`, `confidence`, `symbol`, `source_file`, `evidence`, `explanation`, and `fix_hint`. JAVA/SPRING ONLY.
@@ -656,6 +668,22 @@ cannot be reviewed as one change (on openmrs-core, including it takes the popula
656
668
  — its own, and `migrate-check`'s narrower headline `blocking_count` — because two
657
669
  populations under one name is the defect this project keeps finding in its own output.
658
670
 
671
+ ### `ask migrate-apply` *(supported)*
672
+
673
+ ```bash
674
+ ask migrate-apply . # plan only; no mutation
675
+ ask migrate-apply . --write --rewrite-version X.Y.Z
676
+ ask migrate-apply . --write --rewrite-version X.Y.Z --compile
677
+ ```
678
+
679
+ The default is read-only. `--write` requires a clean Git tree and runs the measured
680
+ recipe in a separate branch/worktree with a pinned OpenRewrite version. The main tree
681
+ is not modified and ASK does not commit or merge. Maven and Gradle are supported
682
+ executors; Gradle uses a temporary OpenRewrite init script and does not modify the
683
+ project build files. After rewriting, ASK runs `migrate-check` on the
684
+ isolated result. `--compile` adds a build with tests skipped and a timeout; network access
685
+ requires `--allow-network`. Any failed stage removes the temporary branch/worktree.
686
+
659
687
  ### `ask data-exposure` *(supported)*
660
688
 
661
689
  **Which routes can carry the data you called sensitive, and who reaches them.** Nothing
@@ -673,9 +701,15 @@ engine never branches on a name. Declare it once, in `sourcecode.config.json`:
673
701
  ```bash
674
702
  ask data-exposure . # every route that can carry a declared label
675
703
  ask data-exposure . --profile prod # …and what THAT deployment leaves reachable
704
+ ask data-exposure . --init --dry-run # suggest candidates, write nothing
705
+ ask data-exposure . --init # write proposed_only candidates under .ask/
676
706
  ask data-exposure /path/to/repo -o exposure.json
677
707
  ```
678
708
 
709
+ `--init` never infers or activates a label. It writes only
710
+ `.ask/data-label-candidates-v1.json`; approved labels still require a human declaration
711
+ in `sourcecode.config.json`.
712
+
679
713
  Everything after the declaration is measured, over authorities that already exist: the
680
714
  call reach `impact-chain` walks, the endpoint security surface `risk` weighs, and the
681
715
  profile resolution `posture` publishes. Two evidence classes, published apart and **never
@@ -1059,6 +1093,7 @@ ask verify # gate: only NEW violations block (--fail-on
1059
1093
  ask verify --capture-baseline # accept today's debt, once
1060
1094
  ask verify --fail-on any # gate on every violation, debt included
1061
1095
  ask verify --fail-on never # report without gating
1096
+ ask verify --format github-actions # native CI annotation; exit contract is unchanged
1062
1097
  ```
1063
1098
 
1064
1099
  **Start with `--init`.** Nobody hand-writes contracts for a 3 000-file monolith, and a gate
@@ -1130,6 +1165,10 @@ The same blast radius, scoped to a diff, with gating exit codes.
1130
1165
  git diff --name-only origin/main | ask pr-impact . --files -
1131
1166
  ask pr-impact . --files changed_files.txt --fail-on high
1132
1167
  ask pr-impact . --files A.java,B.java
1168
+ ask pr-impact . # local git diff HEAD
1169
+ ask pr-impact . --since origin/main # local diff from a base ref
1170
+ ask pr-impact . --pr 123 # opt-in GitHub CLI adapter
1171
+ ask pr-impact . --format github-actions # CI annotations, JSON remains the default
1133
1172
  ```
1134
1173
 
1135
1174
  Reports modified classes, the REST endpoints reachable through their call chains, direct
@@ -1137,6 +1176,10 @@ callers, event publishers and consumers, `@Transactional` methods in the changed
1137
1176
  a consolidated risk level. **A changed Java file that maps to no class caps the verdict at
1138
1177
  `UNKNOWN`** — a partial blast radius is never reported as a low one.
1139
1178
 
1179
+ `--pr NUMBER` invokes the local `gh` CLI to obtain the changed paths. It is opt-in, does not
1180
+ fetch or modify the repository, and requires local GitHub authentication. It cannot be
1181
+ combined with `--files` or `--since`; those modes remain fully offline.
1182
+
1140
1183
  ---
1141
1184
 
1142
1185
  ## Measuring a change, before and after
@@ -1293,7 +1336,9 @@ The MCP server exposes structural analysis tools to AI agents without requiring
1293
1336
 
1294
1337
  ## Output schema
1295
1338
 
1296
- All commands output structured JSON to stdout unless `--output` is specified.
1339
+ Analysis and report commands output structured JSON to stdout unless `--output` is
1340
+ specified. Status and management commands intentionally return concise text, and
1341
+ `ask explain` defaults to text; each command's `--help` names its supported formats.
1297
1342
 
1298
1343
  Common fields:
1299
1344
  - `schema_version` — format version
@@ -1574,11 +1619,12 @@ Allowed formats per command (the first is the default):
1574
1619
  | `endpoints` | `json`, `yaml` | `json` |
1575
1620
  | `validation` | `json`, `yaml` | `json` |
1576
1621
  | `impact-chain` | `json`, `yaml` | `json` |
1577
- | `spring-audit` | `json`, `yaml`, `github-comment` | `json` |
1622
+ | `spring-audit` | `json`, `yaml`, `github-comment`, `github-actions` | `json` |
1578
1623
  | `prepare-context` | `json`, `github-comment` | `json` |
1579
- | `migrate-check` | `json`, `text` | `json` |
1624
+ | `migrate-check` | `json`, `text`, `github-actions` | `json` |
1580
1625
  | `explain` | `text`, `json` | `text` |
1581
- | `pr-impact` | `json`, `text` | `json` |
1626
+ | `pr-impact` | `json`, `text`, `github-actions` | `json` |
1627
+ | `verify` | `json`, `yaml`, `github-actions` | `json` |
1582
1628
 
1583
1629
  `explain` defaults to a human-readable `text` view; pass `-f json` for
1584
1630
  programmatic consumption. `pr-impact` defaults to `json` like every other
sourcecode/archetype.py CHANGED
@@ -134,12 +134,16 @@ class ArchetypeAnalysis:
134
134
  signals_used: list[str]
135
135
  signals_missing: list[str]
136
136
  generated_from: str
137
+ total_files: int
137
138
  graph_metrics: dict[str, float] = field(default_factory=dict)
138
139
 
139
140
  def to_dict(self) -> dict[str, Any]:
140
141
  return {
141
142
  "schema_version": "0.1-experimental",
142
143
  "note": "Parallel/experimental model. Legacy architecture pattern is unchanged.",
144
+ "population": "source files",
145
+ "population_unit": "source files in the scanned repository tree",
146
+ "population_total": self.total_files,
143
147
  "dimensions": {
144
148
  dim: {
145
149
  "primary": r.primary,
@@ -269,6 +273,7 @@ class ArchetypeClassifier:
269
273
  else "endpoint surface not measured"
270
274
  )
271
275
  ),
276
+ total_files=f.total_files,
272
277
  graph_metrics=f.graph_metrics,
273
278
  )
274
279
 
@@ -273,11 +273,51 @@ DEPRECATED_FIELD_REMOVAL = BreakingChange(
273
273
  )
274
274
 
275
275
 
276
+ def _released_schema_removals() -> "list[dict[str, object]]":
277
+ """Fields removed in 5.8.14, kept as an explicit migration record."""
278
+ return [
279
+ {
280
+ "identifier": identifier,
281
+ "becomes": replacement,
282
+ "removed_in": "5.8.14",
283
+ }
284
+ for identifier, replacement in (
285
+ ("migrate-check.findings[].imports_found", "use findings[].evidence"),
286
+ ("ask.mybatis.dto_mappers", "use the compact sibling view or full output"),
287
+ ("ask.mybatis.dto_mappers_total", "use mybatis.mapper_interfaces"),
288
+ ("ask.mybatis.dto_mappers_truncated", "use the view cap metadata"),
289
+ )
290
+ ]
291
+
292
+
293
+ RELEASED_SCHEMA_REMOVALS = BreakingChange(
294
+ id="BC-004",
295
+ kind=KIND_CONTRACT,
296
+ released_in="5.8.14",
297
+ takes_effect_in="5.8.14",
298
+ title="core and supported payload fields removed in a patch release",
299
+ what_changed=(
300
+ "The 5.8.14 payload no longer emits the fields listed in "
301
+ "affected_values. This is a contract change even where the replacement "
302
+ "is a better signal, because consumers must be able to schedule the "
303
+ "migration rather than discover a missing key at runtime."
304
+ ),
305
+ trigger="a consumer reads one of the removed fields from migrate-check or the root ask payload",
306
+ compatibility=(
307
+ "pin 5.8.13 while migrating, or treat each affected field as optional "
308
+ "and read the replacement named beside it"
309
+ ),
310
+ remedy="dispatch on the replacement and gate on its presence before upgrading",
311
+ affected=_released_schema_removals,
312
+ )
313
+
314
+
276
315
  #: Every declared breaking change, newest first. A release that breaks a caller
277
316
  #: and does not add a row here fails `tests/test_breaking_change_
278
317
  #: disclosure_ask11.py` — the policy is enforced, not remembered.
279
318
  BREAKING_CHANGES: tuple[BreakingChange, ...] = (
280
- DEPRECATED_FIELD_REMOVAL, SCHEMA_VERSION_RENAMES, OUTPUT_CEILING,
319
+ RELEASED_SCHEMA_REMOVALS, DEPRECATED_FIELD_REMOVAL,
320
+ SCHEMA_VERSION_RENAMES, OUTPUT_CEILING,
281
321
  )
282
322
 
283
323
 
sourcecode/cache_model.py CHANGED
@@ -297,6 +297,13 @@ COMMANDS: tuple[CommandCache, ...] = (
297
297
  "the parse, not the rule pass.",
298
298
  "not measured on the battery yet — bounded by `migrate-check` on the same "
299
299
  "repository (openmrs-core ~2 s)", analysis_class="repo-wide"),
300
+ CommandCache("migrate-apply", ("parse",), "shared", False,
301
+ "Plan mode reads the same migration findings as `migrate-check`; apply mode "
302
+ "then uses an isolated worktree and never mutates the main tree. A warm can "
303
+ "remove shared parse work, but it does not cache the generated plan or the "
304
+ "optional rewrite/compile execution.",
305
+ "not measured on the battery yet — bounded by `migrate-check` plus the "
306
+ "optional OpenRewrite execution", analysis_class="repo-wide"),
300
307
  CommandCache("data-exposure", ("cir", "parse"), "shared", False,
301
308
  "Walks the same call reach as `impact-chain` once per declared type and "
302
309
  "reads the endpoint security surface, both over the shared CIR a warm "
@@ -0,0 +1,73 @@
1
+ """Small, dependency-free renderers for CI workflow commands."""
2
+ from __future__ import annotations
3
+
4
+
5
+ def _escape(value: object) -> str:
6
+ """Escape values for GitHub Actions command properties and messages."""
7
+ return (
8
+ str(value)
9
+ .replace("%", "%25")
10
+ .replace("\r", "%0D")
11
+ .replace("\n", "%0A")
12
+ .replace(":", "%3A")
13
+ .replace(",", "%2C")
14
+ )
15
+
16
+
17
+ def render_risk_annotation(
18
+ risk_level: str,
19
+ *,
20
+ reason: str = "",
21
+ unresolved: int = 0,
22
+ ) -> str:
23
+ """Render one annotation and a stable summary for a risk report."""
24
+ level = str(risk_level or "UNKNOWN").upper()
25
+ command = {
26
+ "CRITICAL": "error",
27
+ "HIGH": "error",
28
+ "MEDIUM": "warning",
29
+ "LOW": "notice",
30
+ "UNKNOWN": "warning",
31
+ }.get(level, "warning")
32
+ message = f"risk={level}"
33
+ if reason:
34
+ message += f"; {reason}"
35
+ if unresolved:
36
+ message += f"; unresolved_files={unresolved}"
37
+ return f"::{command} title=ASK PR impact::{_escape(message)}"
38
+
39
+
40
+ def render_risk_summary(risk_level: str, *, unresolved: int = 0) -> str:
41
+ """Render a short machine-readable summary line for CI logs."""
42
+ suffix = f" unresolved_files={unresolved}" if unresolved else ""
43
+ return f"ASK_PR_IMPACT risk={str(risk_level or 'UNKNOWN').upper()}{suffix}"
44
+
45
+
46
+ def render_ci_result(
47
+ command: str,
48
+ *,
49
+ status: str,
50
+ severity: str = "unknown",
51
+ count: int = 0,
52
+ reason: str = "",
53
+ partial: bool = False,
54
+ ) -> str:
55
+ """Render one GitHub Actions annotation plus a stable CI summary line."""
56
+ normalized_status = str(status or "unknown").lower()
57
+ level = str(severity or "unknown").upper()
58
+ if partial or normalized_status in {"unverified", "unknown", "partial"}:
59
+ annotation = "warning"
60
+ elif normalized_status in {"fail", "failed", "findings"} or level in {"CRITICAL", "HIGH"}:
61
+ annotation = "error"
62
+ elif level == "MEDIUM":
63
+ annotation = "warning"
64
+ else:
65
+ annotation = "notice"
66
+ message = f"status={normalized_status}; severity={level}; findings={int(count)}"
67
+ if partial:
68
+ message += "; partial=true"
69
+ if reason:
70
+ message += f"; {reason}"
71
+ annotation_line = f"::{annotation} title=ASK {command}::{_escape(message)}"
72
+ summary = f"ASK_CI command={_escape(command)} status={_escape(normalized_status)} severity={_escape(level)} findings={int(count)}"
73
+ return f"{annotation_line}\n{summary}"