This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new e274dd1f69 fix: stabilize manifest fallback and update TODO roadmap
e274dd1f69 is described below

commit e274dd1f699561eeb27604d7340b4c2a8b42759b
Author: James Bognar <[email protected]>
AuthorDate: Mon May 25 08:27:13 2026 -0400

    fix: stabilize manifest fallback and update TODO roadmap
---
 .../rest/convention/BasicVersionResource.java      |  10 +-
 todo/TODO-20-rest-debug-rethink.md                 | 360 ++++++++++++++++-----
 todo/TODO-79-value-annotation-config-bridge.md     | 212 ++++++++++++
 todo/TODO.md                                       |  53 +--
 4 files changed, 521 insertions(+), 114 deletions(-)

diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/convention/BasicVersionResource.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/convention/BasicVersionResource.java
index 77f87bd5fd..23197aad65 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/convention/BasicVersionResource.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/convention/BasicVersionResource.java
@@ -360,12 +360,14 @@ public class BasicVersionResource {
                                        try (var in = u.openStream()) {
                                                var m = new Manifest(in);
                                                // Prefer an 
Implementation-Title-bearing manifest; otherwise hold the
-                                               // first one found as a 
fallback. Walking each candidate is cheap (one
-                                               // open per manifest in the 
resources enumeration).
+                                               // most recent one as a 
fallback. ClassLoader.getResources walks the
+                                               // parent chain FIRST, so 
caller-supplied URLs come last in the
+                                               // enumeration — last-wins 
ensures the explicit classloader argument
+                                               // trumps any unrelated 
MANIFEST.MF leaked from a parent (e.g. JDK
+                                               // module manifests in the boot 
layer, Surefire's manifest-jar, etc.).
                                                if 
(m.getMainAttributes().getValue("Implementation-Title") != null)
                                                        return toMap(m);
-                                               if (fallback == null)
-                                                       fallback = m;
+                                               fallback = m;
                                        }
                                }
                        } catch (IOException e) {
diff --git a/todo/TODO-20-rest-debug-rethink.md 
b/todo/TODO-20-rest-debug-rethink.md
index 4a68015a6b..a4dda8aac8 100644
--- a/todo/TODO-20-rest-debug-rethink.md
+++ b/todo/TODO-20-rest-debug-rethink.md
@@ -2,26 +2,27 @@
 
 Source: promoted from `TODO.md` on 2026-05-21.
 
-## Dependency on TODO-35
+## Dependency on FINISHED-35
 
-Phase 3 of this plan (test-time `@TestBean DebugConfig` overlay) **depends on 
TODO-35
-Phase 2** landing first (`@TestBean` annotation + `JuneauBeanStoreExtension` in
-`juneau-junit5`). Phases 1, 2, and 4 can land independently of TODO-35 — only 
the
-test-side ergonomics in Phase 3 need the BeanStore overlay machinery.
-
-If TODO-35 slips, Phase 3 can still land using direct
-`MockRestClient.Builder.debugConfig(DebugConfig)` wiring (proposed in Phase 3 
below),
-just without the `@TestBean` declarative form.
+Phase 3 of this plan (test-time `@TestBean DebugConfig` overlay) depends on
+**FINISHED-35** (`@TestBean` annotation + `JuneauBeanStoreExtension` in
+`juneau-junit5`) — already landed, so this dep is satisfied at the time of
+plan-update (2026-05-25). All four phases can proceed without external 
blockers.
 
 ## Goal
 
 Replace the current debugging mechanism — split across `DebugEnablement`,
 `CallLogger`'s parallel `normalRules`/`debugRules` lists, five 
`@Rest`/`@RestOp`
 attributes, and a single `Boolean` request attribute — with a **single bean +
-single annotation** surface that:
-
-1. Configures per-endpoint debug **at compile time** (annotation) and **at 
runtime**
-   (injected bean / programmatic `RestRequest` knob).
+single typed annotation slot on `@Rest`/`@RestOp`** that:
+
+1. Configures per-endpoint debug **at compile time** via 
`@Rest(debug=@Debug(...))`
+   and `@RestOp(debug=@Debug(...))` (with standalone `@Debug` as an escape 
hatch
+   for annotation-composition / inherited-`@Rest` scenarios) and **at 
runtime** via
+   an injected `DebugConfig` bean and a programmatic `RestRequest.debug()` 
knob.
+   Putting the debug config inside the `@Rest`/`@RestOp` annotation makes those
+   annotations a **source-of-truth for the resource/op's capabilities** — a 
reader
+   scanning `@Rest(...)` sees every configurable capability in one place.
 2. Carries a **pluggable log format** per matched endpoint 
(text-with-detail-levels,
    JSON, one-line, SLF4J-structured, capture-for-tests, user-supplied).
 3. Plays well with the `BeanStore` so test code can swap the whole debug 
config (or
@@ -201,7 +202,9 @@ other format. Result: `BasicTestCaptureCallLogger` deletion 
costs zero new
 machinery, and tests inspecting captured debug output get the same API as tests
 inspecting any other log channel. See open question #9 below.
 
-### 3. `@Debug` annotation replaces five existing attributes
+### 3. `@Debug` annotation — primary placement on `@Rest`/`@RestOp`, secondary 
placement standalone
+
+The `@Debug` annotation type definition:
 
 ```java
 @Retention(RUNTIME)
@@ -225,14 +228,78 @@ public @interface Debug {
 }
 ```
 
-- Class-level `@Debug(...)` replaces `@Rest(debug)`, `@Rest(debugDefault)`,
-  `@Rest(debugEnablement)`, `@Rest(debugOn)`.
-- Method-level `@Debug(...)` replaces `@RestOp(debug)`.
-- The five existing attributes are deprecated (with identical semantics 
retained
-  internally for one release) and removed in 9.7.
+`@Rest` and `@RestOp` each gain a new typed slot `debug=@Debug(...)`. The slot
+name `debug` is **reused** for the new typed form (the old `String`-typed slot
+is removed in the same release — see §6 below). The old four `@Rest` debug
+attributes (`debug`, `debugDefault`, `debugEnablement`, `debugOn`) collapse 
into
+this single typed slot.
+
+**Primary placement — nested on `@Rest`/`@RestOp` (source-of-truth pattern, 
preferred):**
+
+```java
+@Rest(
+    path="/widgets",
+    debug=@Debug(value="conditional", format=JsonFormat.class)
+)
+public class WidgetResource extends BasicRestServlet {
+
+    @RestGet(path="/", debug=@Debug("always"))
+    public List<Widget> list() { ... }
+}
+```
+
+Putting debug config inside `@Rest`/`@RestOp` makes those annotations the
+canonical source-of-truth for the resource/op's capabilities — a reader 
scanning
+`@Rest(...)` sees every configurable capability in one place rather than having
+to scan for sibling annotations on the same class.
+
+This pattern generalizes beyond debug. Any future configurable capability that
+grows beyond a `String`/`Class` shape should follow the same 
nested-typed-annotation
+placement on `@Rest`/`@RestOp` rather than spawn a new standalone sibling
+annotation. See Open Question #10 below for a candidate follow-on TODO that
+audits the `@Rest`/`@RestOp` surface for other capabilities that could benefit.
+
+**Secondary placement — standalone (escape hatch):**
 
-Compile-time configuration becomes one annotation per place. Runtime 
configuration
-becomes one bean (`DebugConfig`) reachable via the BeanStore.
+```java
+@Debug(value="conditional", format=JsonFormat.class)
+public class WidgetResource extends BaseResource {  // @Rest is inherited from 
BaseResource
+    ...
+}
+```
+
+Standalone `@Debug` on class or method is retained for cases where:
+
+- The target class inherits `@Rest` from a base class and doesn't re-declare 
it.
+- An annotation-composition pattern aggregates debug config independently of 
`@Rest`.
+- A method enters the op-method scan via a custom non-`@RestOp` annotation and
+  still needs debug config.
+
+In all of these the standalone form composes with whatever `@Rest`/`@RestOp`
+configuration is in scope.
+
+**Precedence when both placements are present on the same target:**
+
+`@RestOp(debug=@Debug(...))` on the method beats standalone `@Debug` on the
+method, which beats `@Rest(debug=@Debug(...))` on the class, which beats
+standalone `@Debug` on the class. Specificity wins.
+
+**Mapping table — old attributes → new typed slot:**
+
+| Old | New |
+| --- | --- |
+| `@Rest(debug="true")` | `@Rest(debug=@Debug("always"))` |
+| `@Rest(debug="false")` | `@Rest(debug=@Debug("never"))` |
+| `@Rest(debug="conditional")` | `@Rest(debug=@Debug("conditional"))` |
+| `@Rest(debugDefault="true")` | `@Rest(debug=@Debug("always"))` (collapsed) |
+| `@Rest(debugEnablement=MyEnablement.class)` | 
`@Rest(debug=@Debug(config=MyDebugConfig.class))` |
+| `@Rest(debugOn="MyResource.doX=true")` | 
`@Rest(debug=@Debug(on="MyResource.doX=true"))` |
+| `@RestOp(debug="true")` | `@RestOp(debug=@Debug("always"))` |
+| `@RestOp(debug="conditional")` | `@RestOp(debug=@Debug("conditional"))` |
+
+Compile-time configuration becomes one annotation slot (`debug=@Debug(...)`) on
+`@Rest`/`@RestOp` — with optional standalone `@Debug` for escape hatches. 
Runtime
+configuration becomes one bean (`DebugConfig`) reachable via the BeanStore.
 
 ### 4. Runtime fluent surface on `RestRequest`
 
@@ -290,23 +357,76 @@ dial-up of `level` / `format` is just a builder call.
 `MockRestClient.Builder.debugConfig(DebugConfig)` method — less ergonomic, but
 self-contained.
 
-### 6. Migration
-
-- **9.5.x (current release line)** — land the new `DebugConfig` + `@Debug` +
-  `DebugFormat` alongside the existing system. `BasicDebugEnablement` and
-  `BasicCallLogger` are reimplemented as thin adapters that read 
`@Rest(debug)` /
-  `@RestOp(debug)` / `@Rest(debugOn)` into a `DebugConfig` builder internally.
-  Existing tests and resources keep working with **zero code changes**.
-- **9.5.x release notes** — entry in
-  `juneau-docs/docs/pages/release-notes/9.5.0.md` (or 9.5.1 if that's open) 
under
-  `juneau-rest-server`: new annotation, new bean, deprecation note for the old
-  surface.
-- **9.6** — deprecate `@Rest(debug)`, `@Rest(debugDefault)`,
-  `@Rest(debugEnablement)`, `@Rest(debugOn)`, `@RestOp(debug)`, 
`DebugEnablement`,
-  `BasicDebugEnablement`, `BasicCallLogger`'s `normalRules` / `debugRules`.
-  Migration entry in `juneau-docs/pages/topics/23.01.V9.5-migration-guide.md`
-  (TODO-17 territory) with Old → New rows.
-- **9.7** — remove deprecated surface.
+### 6. Migration — hard break, no deprecation cycle
+
+**Decision (2026-05-25):** no two-release deprecation cycle. The old debug
+surface is **removed in 9.5** in the same release that lands the new
+`@Debug` / `DebugConfig` / `DebugFormat` surface. Migration is documented but
+not automated.
+
+**Rationale:** the old surface is a five-attribute mess (`debug`, 
`debugDefault`,
+`debugEnablement`, `debugOn` on `@Rest`, plus `debug` on `@RestOp`) with a
+transitional 9.5 wart already in it (the `debug` vs `debugDefault` distinction
+landed mid-release). Carrying it as deprecated-but-functional for a 9.6 → 9.7
+window would mean shipping two parallel code paths (the back-compat adapter
+chain reading old attributes into a new `DebugConfig` builder, plus the new
+typed surface) for a year, doubling the surface area for tests, docs, and
+review. The hard break trades one explicit migration step at the 9.5 boundary
+for a cleaner long-term shape.
+
+**What gets removed in 9.5 (this TODO's PR):**
+
+- `@Rest(debug)` (String) — replaced by `@Rest(debug=@Debug(...))`.
+- `@Rest(debugDefault)` (String) — collapsed into `@Rest(debug=@Debug(...))`.
+- `@Rest(debugEnablement)` (Class) — replaced by
+  `@Rest(debug=@Debug(config=...))`.
+- `@Rest(debugOn)` (String) — replaced by `@Rest(debug=@Debug(on="..."))`.
+- `@RestOp(debug)` (String) — replaced by `@RestOp(debug=@Debug(...))`.
+- `DebugEnablement` class + `BasicDebugEnablement` impl — replaced by
+  `DebugConfig` + `BasicDebugConfig`.
+- `CallLogger.Builder.normalRules` / `debugRules` — collapsed into
+  `DebugConfig.rule(...)` builders with per-rule format + level.
+- `BasicCallLogger`'s parallel-rule wiring — replaced by single-rule resolve
+  through `DebugConfig`.
+- `BasicTestCallLogger`, `BasicTestCaptureCallLogger` — replaced by
+  `CapturingFormat` (or by direct `LogRecordCapture` use per OQ #9).
+
+**What gets preserved:**
+
+- `RestRequest.setDebug()` / `setDebug(Boolean)` / `isDebug()` — kept as
+  one-liner shortcuts over the new `req.debug()` fluent surface.
+- `CallLogger` class itself — refactored to delegate to `DebugConfig`, not
+  removed.
+- System-property knobs (`juneau.restLogger.*` / `JUNEAU_RESTLOGGER_*`) — kept;
+  they map onto `DebugConfig` builder defaults at construction time.
+
+**Migration notes (delivered with the PR):**
+
+A new migration-guide section lands in
+`juneau-docs/pages/topics/23.01.V9.5-migration-guide.md` (TODO-17 territory)
+with explicit Old → New rows covering every removed surface. Sections:
+
+1. **Annotation migration** — Old → New table mirroring the mapping table in
+   §3 above, with side-by-side code samples.
+2. **`DebugEnablement` → `DebugConfig` migration** — for the (small) population
+   of users with a custom `DebugEnablement` subclass: pattern for porting
+   `ReflectionMap<Enablement>` lookups onto `DebugConfig.Builder.rule(...)`.
+3. **`CallLogger` rule-list migration** — pattern for porting parallel
+   `normalRules` / `debugRules` setups onto unified `DebugRule` instances that
+   carry both gating predicate and format.
+4. **Test-side migration** — for users with `BasicTestCallLogger` /
+   `BasicTestCaptureCallLogger` subclasses: pattern for replacing them with
+   `CapturingFormat` (or with direct `LogRecordCapture` per OQ #9 once
+   resolved).
+5. **System-property migration** — none required; the `juneau.restLogger.*`
+   knobs continue to work and now feed the new `DebugConfig` builder.
+
+**Release-notes entry** in `juneau-docs/docs/pages/release-notes/9.5.0.md`
+under `### juneau-rest-server` with a `**Breaking change**` callout pointing
+at the migration-guide section.
+
+Future-release impact: 9.6 onwards has no carried-over deprecated debug
+surface to remove — TODO-20 lands clean in one shot.
 
 ### 7. Class summary
 
@@ -318,7 +438,9 @@ self-contained.
 | `DebugFormat` (interface) | `org.apache.juneau.rest.debug` | 
`juneau-rest-server` | Pluggable formatter. |
 | `BasicTextFormat`, `OneLineFormat`, `JsonFormat`, `CapturingFormat` | 
`org.apache.juneau.rest.debug.format` | `juneau-rest-server` | Built-in 
formats. |
 | `Slf4jStructuredFormat` | `org.apache.juneau.rest.debug.format.slf4j` | new 
`juneau-rest-server-slf4j` | SLF4J-only sub-module; keeps core SLF4J-free. |
-| `@Debug` | `org.apache.juneau.rest.annotation` | `juneau-rest-server` | 
Replaces 5 existing attributes. |
+| `@Debug` | `org.apache.juneau.rest.annotation` | `juneau-rest-server` | Used 
both as `@Rest(debug=@Debug(...))` / `@RestOp(debug=@Debug(...))` (primary, 
source-of-truth) and standalone on class/method (secondary, escape hatch). |
+| `@Rest.debug` slot type change | `org.apache.juneau.rest.annotation` | 
`juneau-rest-server` | `String` → `@Debug`. Hard break — see §6. |
+| `@RestOp.debug` slot type change | `org.apache.juneau.rest.annotation` | 
`juneau-rest-server` | `String` → `@Debug`. Hard break — see §6. |
 | `RestRequest.debug()` fluent | `org.apache.juneau.rest` | 
`juneau-rest-server` | New runtime knob; `setDebug`/`isDebug` retained as 
shortcuts. |
 
 ## Open questions
@@ -357,11 +479,15 @@ self-contained.
    structured form (`@Debug.On({ @Debug.Rule(targets=…, value=…), …})`)?
    Recommendation: keep the string form for SVL-resolved system-property use 
cases;
    add `@Debug.Rule` for the in-source form.
-9. **Integrate `org.apache.juneau.commons.logging` package**. The existing
-   `juneau-commons` logging package (`Logger`, `LogRecord`, 
`LogRecordListener`,
-   `LogRecordCapture`) is the right substrate for the new debug surface to sit 
on:
-   - **`CapturingFormat`** is a thin wrapper around `LogRecordCapture` (see
-     section 4 above) — `try-with-resources` capture, format-string assertions,
+9. **Integrate `org.apache.juneau.commons.logging` package + `CapturingFormat` 
vs
+   `LogRecordCapture`**. Two related sub-questions, captured together for
+   readability.
+
+   **(a) Substrate.** The existing `juneau-commons` logging package
+   (`Logger`, `LogRecord`, `LogRecordListener`, `LogRecordCapture`) is the 
right
+   substrate for the new debug surface to sit on:
+   - **`CapturingFormat`** is a thin wrapper around `LogRecordCapture` (see §2
+     above) — `try-with-resources` capture, format-string assertions,
      `LogRecordListener` plumbing, all already exist.
    - **`DebugConfig.logger` field** should accept either a 
`java.util.logging.Logger`
      (back-compat with today's `CallLogger.Builder.logger(...)`) **or** a
@@ -372,25 +498,70 @@ self-contained.
      (`{level}: {msg}`, `{thrown}`, etc.) should be the reference set for any
      placeholder vocabulary `BasicTextFormat` / `OneLineFormat` / `JsonFormat`
      end up exposing.
+
    Recommendation: explicit dependency on `org.apache.juneau.commons.logging` 
from
    `org.apache.juneau.rest.debug`; no parallel reinvention of listener / 
capture /
    formatted-message machinery.
-9. **`CapturingFormat` vs `LogRecordCapture`**. Two paths:
+
+   **(b) `CapturingFormat` vs `LogRecordCapture`.** Two paths:
    1. Ship a dedicated `CapturingFormat` with its own `AtomicReference<String>`
       mechanism (the original draft above).
-   2. **Recommended:** route every `DebugFormat` through 
`org.apache.juneau.commons.logging.Logger`
-      (already wraps `java.util.logging.Logger` and supports 
`addLogRecordListener` +
+   2. **Recommended:** route every `DebugFormat` through
+      `org.apache.juneau.commons.logging.Logger` (already wraps
+      `java.util.logging.Logger` and supports `addLogRecordListener` +
       `captureEvents()`), and have tests use `LogRecordCapture` directly — no
       bespoke capturing format needed. The capturing API the user already has
-      (`logger.captureEvents()` returning a `LogRecordCapture` `Closeable`) is 
the
-      idiomatic test surface. The new `DebugConfig.Builder.logger(Logger)` 
would
-      accept `org.apache.juneau.commons.logging.Logger` (the framework's
-      delegating logger) so this composes for free.
+      (`logger.captureEvents()` returning a `LogRecordCapture` `Closeable`) is
+      the idiomatic test surface.
+
    Cost of (2): we lose the "format-output-as-string" capture niche (e.g. 
capture
    the exact JSON the prod system would have emitted, byte-for-byte). 
Mitigation:
    `LogRecord` already carries the rendered message; tests that need the 
rendered
    string use `cap.getRecords("{msg}")`. Decision needed before Phase 1.
 
+10. **Source-of-truth audit follow-on TODO** (new, surfaced 2026-05-25). User 
has
+    articulated a broader design principle: *"we want the `@Rest` and `@RestOp`
+    annotations to be a source-of-truth for capabilities."* TODO-20 implements
+    this principle for debug (§3 above). Are there other capabilities on
+    `@Rest`/`@RestOp` today that have grown beyond their original 
`String`/`Class`
+    slot shapes and would benefit from the same nested-typed-annotation 
upgrade?
+    Quick scan of candidates (not exhaustive):
+    - `@Rest(callLogger=Class)` → potentially 
`@Rest(callLogger=@CallLogger(...))`
+      if `@CallLogger` ever grows fields beyond a class reference. Today it's
+      just a class, so no migration needed. **Re-evaluate after TODO-20 lands**
+      — if `DebugConfig` ends up subsuming most `CallLogger` configuration, the
+      `callLogger` slot may shrink further or be eliminated.
+    - `@Rest(swagger=...)` / `@Rest(openApi=...)` (FINISHED-74 territory) —
+      these are already nested annotation types. Already 
source-of-truth-shaped.
+      No action.
+    - `@Rest(properties=...)` / `@Rest(beanProperties=...)` — already nested
+      annotation types. Already source-of-truth-shaped. No action.
+    - `@Rest(rolesDeclared)`, `@Rest(roleGuard)` — `String` slots that interact
+      with the AuthN guards landed in FINISHED-69. Worth re-evaluating once
+      TODO-69's `@Auth` surface is in user hands — they could become
+      `@Rest(auth=@Auth(...))`-shaped if the roles-vs-AuthN-guard division 
turns
+      out to be ergonomically awkward.
+
+    Recommendation: file a follow-on **TODO-90 — `@Rest`/`@RestOp` 
source-of-truth
+    annotation pattern audit** after TODO-20 lands, to do a systematic pass 
over
+    every slot on both annotations and identify candidates for the
+    nested-typed-annotation upgrade. Don't scope-creep TODO-20 to do this audit
+    now — it would balloon the PR.
+
+11. **Confirm the hard-break decision boundary**. §6 commits to a hard break in
+    9.5 (this TODO's PR) — no deprecation cycle. This is consistent with the
+    user's 2026-05-25 direction. Two micro-confirmations worth raising before
+    Phase 2 starts:
+    - **`RestRequest.setDebug()` / `setDebug(Boolean)` / `isDebug()`** — kept
+      per §6. Confirm these stay as one-liner shortcuts and do NOT get removed
+      alongside the annotation surface.
+    - **System-property knobs** (`juneau.restLogger.*` / `JUNEAU_RESTLOGGER_*`)
+      — kept per §6. Confirm these continue to feed `DebugConfig` builder
+      defaults, even though the underlying bean shape changes.
+
+    Recommendation: both kept. The annotation surface is the breaking change;
+    the runtime/operator surface stays stable.
+
 ## Out of scope
 
 - Rewriting `juneau-rest-client`'s debug surface (`RestClient.Builder.debug()` 
/
@@ -421,46 +592,67 @@ self-contained.
    - `DebugFormat_Test` — each built-in produces the expected output.
    - `CallLogger_DebugConfig_Test` — end-to-end through `MockRestClient`.
 
-### Phase 2 — `@Debug` annotation + adapter on top of existing annotations
-
-1. Add `@Debug` annotation (class + method scope).
-2. `BasicDebugEnablement` reads `@Debug` first, falls back to `@Rest(debug)` /
-   `@RestOp(debug)` / `@Rest(debugOn)` for unchanged user code.
-3. `RestContext.debugEnablement` memoizer simplifies: collapse the `debug` /
-   `debugDefault` priority dance to a single `@Debug.value()` read.
-4. Migrate `Rest_Debug_Test` cases over (the file is the canonical 1085-line
-   covers-everything test — keep the existing matrix, add a parallel
-   `@Debug`-based matrix).
+### Phase 2 — `@Debug` annotation + new typed slots on `@Rest`/`@RestOp` (hard 
break)
+
+Per §6, this phase **removes** the old debug surface in the same step that 
lands
+the new one — no back-compat adapter, no two-release deprecation cycle.
+
+1. Add `@Debug` annotation (class + method scope per §3).
+2. Add typed `debug=@Debug(...)` slot to `@Rest` and `@RestOp`. Old `String`
+   `debug` / `debugDefault` / `debugEnablement` / `debugOn` attributes on 
`@Rest`
+   and `String debug` on `@RestOp` are **deleted** in this step (not 
deprecated).
+3. `RestContext.debugEnablement` memoizer is **deleted**. The new
+   `RestContext.debugConfig` memoizer reads `@Rest(debug=@Debug(...))`,
+   `@RestOp(debug=@Debug(...))`, and standalone `@Debug` per the precedence 
rules
+   in §3, builds a `DebugConfig`, publishes it into the BeanStore.
+4. `Rest_Debug_Test` (the canonical 1085-line covers-everything test) is
+   **rewritten** to use the new annotation surface — the existing test matrix
+   moves over to `@Rest(debug=@Debug(...))` / `@RestOp(debug=@Debug(...))` /
+   standalone `@Debug` placements. No "parallel matrix" — the old surface no
+   longer exists.
 5. Tests:
-   - `Debug_Annotation_Test` — full annotation matrix (mirrors today's
-     `Rest_Debug_Test`, on the new annotation).
-   - `Debug_BackCompat_Test` — confirms old `@Rest(debug)` / `@Rest(debugOn)` /
-     `@RestOp(debug)` still produce identical behavior.
+   - `Debug_Annotation_Test` — full annotation matrix on the new surface, 
covering
+     all three placement options (nested-on-`@Rest`, nested-on-`@RestOp`, 
standalone)
+     and the precedence rules from §3.
+   - `Debug_SourceOfTruth_Test` — confirms that the source-of-truth principle
+     reads correctly: a single `@Rest(debug=@Debug(...))` produces the same
+     resolved `DebugConfig` as the equivalent standalone `@Debug` on the class.
 
-### Phase 3 — runtime fluent surface + TODO-35 integration
+### Phase 3 — runtime fluent surface + FINISHED-35 integration
 
-**Depends on TODO-35 Phase 2 for the `@TestBean` form.** Direct
-`MockRestClient.Builder.debugConfig(DebugConfig)` wiring can land 
independently.
+**FINISHED-35** is landed, so both forms below ship in this phase.
 
 1. Add `RestRequest.debug()` fluent (returns a `DebugScope` per-request 
handle).
    Keep `setDebug` / `isDebug` as one-liners on top of it.
 2. Add `MockRestClient.Builder.debugConfig(DebugConfig)` for direct test wiring
-   (parallel to TODO-35's `.overridingBeanStore(...)`).
+   (parallel to FINISHED-35's `.overridingBeanStore(...)`).
 3. Tests:
    - `Debug_Runtime_Test` — `req.debug().enable(JsonFormat.class).level(FINE)`
      works.
-   - `Debug_TestBean_Test` (in `juneau-utest`, after TODO-35 Phase 2 lands) — 
uses
-     `@TestBean DebugConfig` + `JuneauBeanStoreExtension` to swap formats and
-     capture output.
-
-### Phase 4 — deprecate the old surface (9.6)
-
-1. Add `@Deprecated(since="9.6", forRemoval=true)` to `@Rest(debug)`,
-   `@Rest(debugDefault)`, `@Rest(debugEnablement)`, `@Rest(debugOn)`,
-   `@RestOp(debug)`, `DebugEnablement`, `BasicDebugEnablement`,
-   `CallLogger.Builder.normalRules` / `debugRules`, `BasicCallLogger`'s 
per-status
-   rule wiring.
-2. Release-notes + migration-guide entries
-   (`juneau-docs/docs/pages/release-notes/9.5.0.md` and
-   `juneau-docs/pages/topics/23.01.V9.5-migration-guide.md`).
-3. No code removal yet — that's the 9.7 cycle.
+   - `Debug_TestBean_Test` — uses `@TestBean DebugConfig` +
+     `JuneauBeanStoreExtension` (from FINISHED-35) to swap formats and capture
+     output without rebuilding `MockRestClient`.
+
+### Phase 4 — migration notes + release notes (juneau-docs)
+
+No code removal here — Phase 2 already removed the old surface. This phase is
+documentation-only.
+
+1. New migration-guide section in
+   `juneau-docs/pages/topics/23.01.V9.5-migration-guide.md` (TODO-17 territory)
+   covering the five Old → New migration tracks per §6:
+   - Annotation migration (Old → New table from §3).
+   - `DebugEnablement` → `DebugConfig` migration (custom-subclass porting 
pattern).
+   - `CallLogger` rule-list migration (parallel `normalRules`/`debugRules` →
+     unified `DebugRule`).
+   - Test-side migration (`BasicTestCallLogger` / `BasicTestCaptureCallLogger` 
→
+     `CapturingFormat` or `LogRecordCapture` per OQ #9).
+   - System-property migration (none required; auto-mapped).
+2. Release-notes entry in `juneau-docs/docs/pages/release-notes/9.5.0.md` under
+   `### juneau-rest-server` with a `**Breaking change**` callout pointing at 
the
+   migration-guide section.
+3. New topic page or sub-section under
+   `juneau-docs/pages/topics/10.20.RestServerDebug.md` (or equivalent slot — 
pick
+   the cleanest spot in the existing topics tree) walking through the new
+   `@Debug` placement options, the source-of-truth principle, and worked
+   examples for each `DebugFormat` built-in.
diff --git a/todo/TODO-79-value-annotation-config-bridge.md 
b/todo/TODO-79-value-annotation-config-bridge.md
new file mode 100644
index 0000000000..b88c7a0874
--- /dev/null
+++ b/todo/TODO-79-value-annotation-config-bridge.md
@@ -0,0 +1,212 @@
+# TODO-79: Juneau `@Value` annotation + Spring Boot `application.yaml` bridge
+
+Source: TODO.md headline bullet expanded 2026-05-25 after a brainstorming 
session that mapped the work against the existing 
`org.apache.juneau.commons.settings` + `org.apache.juneau.commons.inject` 
infrastructure.
+
+## Goal
+
+Add a Juneau `@Value` annotation that lets beans, fields, setters, and 
constructor parameters read configuration values declaratively (analog to 
Spring's `@Value`), plus a `${xxx}` shortcut in `VarResolver` and a Spring 
`Environment` bridge so that values defined in `application.yaml` / 
`application.properties` are reachable through the same 
`@Value`/`$P{...}`/`Settings` lookup pipeline as native Juneau `*.cfg` entries.
+
+End-state developer experience:
+
+```java
+// Native Juneau:
+@Rest(path="/orders")
+public class OrdersResource extends RestServlet {
+
+    @Value("${db.url}")                          // From juneau.cfg, system 
props, env, or Spring Environment
+    private String dbUrl;
+
+    @Value("${db.timeout.ms:5000}")              // Default of 5000 if unset, 
coerced to int
+    private int timeoutMs;
+
+    @Inject
+    public OrdersResource(
+        @Value("${app.name:orders}") String appName,
+        @Value("${app.startInstant}") Instant startInstant   // Coerced via 
Settings.toType(...)
+    ) { ... }
+}
+
+// Mixed Juneau + Spring `@Value` (FQN-detected, no compile-time Spring dep in 
juneau-commons):
+@Rest(path="/billing")
+public class BillingResource extends RestServlet {
+
+    @org.springframework.beans.factory.annotation.Value("${stripe.api.key}")
+    private String stripeKey;   // Honored identically to Juneau's @Value
+}
+
+// Free-text resolution via the new shortcut:
+String banner = restRequest.getVarResolver().resolve("Welcome to 
${app.name}!");
+```
+
+## Why now
+
+- The `org.apache.juneau.commons.settings.PropertySource` SPI already exists 
and is already implemented by `org.apache.juneau.config.ConfigPropertySource`. 
The interface seam between `juneau-commons` and `juneau-config` that the 
brainstorming session worried about is already in the tree — TODO-79 is mostly 
**wiring**, not new infrastructure.
+- `Settings.useServiceLoader()` already calls 
`ServiceLoader.load(PropertySourceProvider.class)` at build time (see 
[Settings.java](../juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Settings.java)
 lines 281-290), but `juneau-config` ships **no** `PropertySourceProvider` and 
**no** `META-INF/services/...PropertySourceProvider` entry, so the wire is 
unconnected.
+- The `$P{key,default}` var in 
[PropertyVar.java](../juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/vars/PropertyVar.java)
 already routes through `Settings.get()` — the unified lookup point. Making 
`${xxx}` a shortcut for `$P{xxx}` is a 3-line tokenizer tweak in 
`VarResolverSession`, not a new var.
+- `BasicBeanStore` + `BeanInstantiator` already drive `@Bean` / `@Inject` / 
`@Named` / `@Autowired` / `@ConditionalOnProperty` field/parameter/setter 
resolution via FQN-based annotation matching in 
[JsrSupport.java](../juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/JsrSupport.java).
 Adding `@Value` slots in next to `@Inject` in the same pipeline — and 
FQN-matching `org.springframework.beans.factory.annotation.Value` falls out for 
free with the same trick that a [...]
+- TODO-69 (AuthN guards) is done, TODO-20 (debug rethink) is next, TODO-78 + 
view siblings are queued. TODO-79 sits in Phase G (server-feature track) and 
has no hard dependencies on any of those — can land any time after Phase A.
+
+## Research findings (verified 2026-05-25)
+
+Significant facts from reading the current tree that shape the design:
+
+1. **`PropertySource` SPI is in `juneau-commons`** 
([PropertySource.java](../juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/PropertySource.java)).
 Returns a `PropertyLookupResult` that distinguishes "missing" from 
"present-but-null". Already the neutral interface between `juneau-commons` and 
`juneau-config`.
+
+2. **`ConfigPropertySource` exists in `juneau-config`** 
([ConfigPropertySource.java](../juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigPropertySource.java))
 and adapts `Config` to `PropertySource`. It's a 50-line file, no other 
adapters needed.
+
+3. **`Settings` already aggregates sources** 
([Settings.java](../juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/settings/Settings.java)).
 Lookup order is documented at lines 44-53: thread-local override → global 
override → sources in reverse insertion order → system props → env. Already 
supports type coercion via `StringSetting.asType(Class)` for `Integer`, `Long`, 
`Boolean`, `Double`, `Float`, `File`, `Path`, `URI`, `Charset`, plus 
reflection-based fallback to `value [...]
+
+4. **`$P{...}` is already the Settings-backed var** 
([PropertyVar.java](../juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/vars/PropertyVar.java)).
 Subclass of `DefaultingVar` so `$P{key,default}` syntax is already supported.
+
+5. **`JsrSupport` already does FQN-based annotation detection** 
([JsrSupport.java](../juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/JsrSupport.java)).
 Recognizes Juneau, Jakarta, JavaX, and Spring variants of `@Inject` / 
`@Autowired` / `@Named` / `@Qualifier` / `@Singleton` / `@PostConstruct` / 
`@PreDestroy` without compile-time dependencies on those APIs. Same trick 
applies trivially to `@Value`.
+
+6. **`SpringBeanStore` is the natural Spring seam** 
([SpringBeanStore.java](../juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringBeanStore.java))
 — already captures `ApplicationContext` in its constructor (line 66). 
`ApplicationContext.getEnvironment()` gives the `Environment` that the Spring 
bridge needs to wrap.
+
+7. **No 
`META-INF/services/org.apache.juneau.commons.settings.PropertySourceProvider` 
exists anywhere in the tree** (`rg` confirmed). `juneau-config`'s 
`ConfigPropertySource` is therefore never auto-registered today; consumers must 
wire it manually.
+
+8. **JDK ships no usable EL** (verified separately). Nashorn was removed in 
JDK 15; `javax.script` is empty without a third-party engine; `StringTemplate` 
(JEP 430) was preview in JDK 21-23 and **withdrawn in JDK 24** pending 
redesign. Jakarta EL is a separate dep (`org.glassfish:jakarta.el`), SpEL is 
`spring-expression`. **For TODO-79 we deliberately ship Tier-1 placeholder 
semantics only** (key + default + nesting). `#{...}` expression support is 
deferred to a future `juneau-config-el` [...]
+
+9. **`VarResolverSession`'s tokenizer matches `$X{...}`** where `X` is a Var 
name (zero or more ASCII chars). The `${...}` shortcut is a single lookahead in 
the scanner: when `$` is followed immediately by `{`, emit `P` as the var name. 
Backward-compatible — no existing `$X{...}` form changes.
+
+## Resolved decisions (from brainstorming 2026-05-25)
+
+1. **`@Value` lives in `org.apache.juneau.commons.inject`** alongside 
`@Inject` / `@Named` / `@Bean`, not in `juneau-microservice`. Symmetric to 
`@Inject` (resolves *beans*); `@Value` resolves *strings/primitives*. Spring 
places `@Value` in `org.springframework.beans.factory.annotation` alongside 
`@Autowired`, not in `spring-boot` — same precedent.
+
+2. **No new interface layer between `juneau-commons` and `juneau-config`.** 
`PropertySource` already is the layer. We just need a 
`ConfigPropertySourceProvider` + `META-INF/services` entry in `juneau-config` 
to flip on the auto-wiring that `Settings.useServiceLoader()` already calls.
+
+3. **Shortcut syntax: `${xxx}` → `$P{xxx}`.** Routes through `Settings` so 
Config + env + system properties + Spring `Environment` all participate via the 
same lookup. Spring-idiom matches industry mental model; literal 
`@Value("${db.url}")` is the same string as Spring's, zero learning curve. 
Sidesteps the `MessageFormat`/`Mustache`/SLF4J `{0}` collision that a bare 
`{xxx}` shortcut would have.
+
+4. **Expression scope: Tier 1 only** (property-placeholder semantics — key, 
default, nesting). No arithmetic, no method calls, no ternary. 
`${foo.${env}.url}` works for free because `VarResolver` already resolves inner 
vars before passing the key to the outer var. `#{...}` EL deferred to a 
separate follow-on TODO if/when demand surfaces.
+
+5. **Spring `@Value` is honored identically to Juneau `@Value`** via 
FQN-matching in `JsrSupport`. No compile-time Spring dep in `juneau-commons`. 
Both annotations carry the same `${...}` payload, both resolve through the same 
`Settings`-backed pipeline.
+
+6. **`@Value` and `@Inject` are mutually exclusive on the same injection 
site.** Throw `BeanCreationException` with a clear message if both are present. 
`@Value` for strings/primitives, `@Inject` for beans — no overloaded semantics.
+
+7. **`ConfigPropertySourceProvider` is classpath-default-silent.** If 
`juneau.cfg` is not on the classpath, return `null` from `create()` (already 
filtered out by the SPI's `.filter(Objects::nonNull)` stream). No warning log. 
Many deployments will not want a classpath config and shouldn't see noise.
+
+## Architecture
+
+```mermaid
+graph TB
+  subgraph commons [juneau-commons]
+    Value["@Value (NEW)"]
+    JsrSupp[JsrSupport]
+    BeanInst[BeanInstantiator]
+    Settings
+    PVar["$P / ${...} (UPDATED)"]
+    PSP[PropertySourceProvider SPI]
+  end
+  subgraph config [juneau-config]
+    Config
+    CPS[ConfigPropertySource]
+    CPSP["ConfigPropertySourceProvider (NEW)"]
+    services["META-INF/services entry (NEW)"]
+  end
+  subgraph spring [juneau-rest-server-springboot]
+    SpringEnvPS["SpringEnvironmentPropertySource (NEW)"]
+  end
+  BeanInst -- "@Value -> string" --> Settings
+  PVar --> Settings
+  Settings -- "ServiceLoader" --> PSP
+  CPSP --> CPS
+  CPS --> Config
+  services -.declares.-> CPSP
+  SpringEnvPS --> Settings
+```
+
+Lookup precedence inside `Settings` is already "sources in reverse insertion 
order, then sys-props, then env". Spring `Environment` is added last by the 
Spring auto-config, so it wins over `Config`, which wins over sys-props, which 
wins over env.
+
+## Scope
+
+**In scope (v1):**
+
+- **`org.apache.juneau.commons.inject.Value`** annotation. `@Target({FIELD, 
METHOD, PARAMETER, CONSTRUCTOR})`, `@Retention(RUNTIME)`, single `String 
value()` attribute carrying a `${...}` expression or plain literal.
+- **`JsrSupport` extensions**: `JUNEAU_VALUE`, `SPRING_VALUE` FQN constants + 
`isValueAnnotation(AnnotationInfo)` and `valueExpression(AnnotationInfo)` 
helpers.
+- **`BeanInstantiator` parameter/field/setter resolution branch** that detects 
`@Value` (Juneau or Spring), resolves the expression through a session-scoped 
`VarResolver`, and coerces to the target type via `Settings.toType(String, 
Class)`.
+- **`${xxx}` shortcut** in `VarResolverSession`'s tokenizer — single lookahead 
that lowers `${...}` to `$P{...}`.
+- **`org.apache.juneau.config.ConfigPropertySourceProvider`** + 
`META-INF/services/org.apache.juneau.commons.settings.PropertySourceProvider` 
entry in `juneau-config`. Returns `new 
ConfigPropertySource(Config.create().name("juneau.cfg").build())` if a 
classpath `juneau.cfg` exists; `null` otherwise.
+- **Microservice + RestContext integration**: when a microservice/RestContext 
resolves its own `Config`, push a `ConfigPropertySource` wrapping it onto 
`Settings.get().addSource(...)` so per-microservice config shadows the 
classpath default.
+- **`org.apache.juneau.rest.springboot.SpringEnvironmentPropertySource`** 
wrapping `org.springframework.core.env.Environment`. Auto-registered from the 
existing Spring `@Configuration` (or `SpringBeanStore` constructor) so 
`application.yaml` / `application.properties` / profile-specific overrides / 
CLI args participate.
+- **Tests** in `juneau-utest` covering: `String` / `int` / `Integer` / 
`boolean` / `URI` / `Instant` coercion; `${foo}`, `${foo:bar}`, 
`${foo.${env}.url}` resolution; `@Value` + Spring `@Value` interchangeability; 
per-microservice-config shadowing; Spring `Environment` shadowing of `Config`.
+- **Docs**: release-notes entry under 
`juneau-docs/pages/release-notes/9.5.0.md` + new topic page documenting 
`@Value` + `${...}` + Spring Boot bridge.
+
+**Explicitly out of scope (v1):**
+
+- **`#{...}` SpEL / Jakarta-EL expressions.** Deferred to a future 
`juneau-config-el` module if demand surfaces. The architecture leaves room: 
`#{...}` would lower to `$EL{...}` the same way `${...}` lowers to `$P{...}`.
+- **`@ConfigurationProperties`-style prefix binding** (binding `app.db.*` to a 
`DbConfig` bean). `@Value` is single-key. Prefix binding is a separate effort 
if requested.
+- **Arithmetic / boolean / ternary inside `${...}`.** Pure placeholder 
semantics only.
+- **Auto-registering a non-classpath `Config`.** 
`ConfigPropertySourceProvider` only looks for `juneau.cfg` on the classpath; 
programmatic `Config` instances are wired via the Microservice/RestContext 
integration path, not the SPI.
+- **Hot-reload of `@Value`-injected fields when the backing `Config` 
changes.** Spring's `@Value` is also single-shot at injection time. `Config` 
already has a `ConfigEventListener` for hot-reload-aware code that wants it; 
`@Value` users who need that should observe `Config` directly.
+
+## Implementation plan
+
+### Phase 1 — `@Value` annotation + BeanInstantiator wiring (juneau-commons)
+
+- Create 
`juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/Value.java`.
+- Extend 
[JsrSupport.java](../juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/JsrSupport.java)
 with `JUNEAU_VALUE` and `SPRING_VALUE` FQN constants plus 
`isValueAnnotation(AnnotationInfo)` and `valueExpression(AnnotationInfo)` 
helpers. Mirrors the existing `isNamedAnnotation` / `qualifierValue` pattern.
+- In 
[BeanInstantiator.java](../juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanInstantiator.java),
 in the parameter/field/setter resolution path that today checks `@Inject` / 
`@Autowired` / `@Named`:
+    - If an injection site carries `@Value` (Juneau or Spring), reject it if 
`@Inject` is also present (throw `BeanCreationException`).
+    - Otherwise resolve the expression through a session-scoped `VarResolver` 
(default vars + `PropertyVar` is enough — `${...}` will already be the shortcut 
from Phase 2), then coerce to the target type via `Settings.toType(String, 
Class)`.
+    - Wrap coercion failures in a `BeanCreationException` with a clear "could 
not coerce '${expr}' resolving to '<value>' to <targetType>" message.
+- Acceptance tests in 
`juneau-utest/src/test/java/org/apache/juneau/commons/inject/Value_Test.java`:
+    - `@Value` on constructor parameter, setter, field — all three sites.
+    - String / int / Integer / boolean / URI / Instant target types.
+    - `${missing:default}` path — default used.
+    - `${missing}` with no default — `null` for reference types, 
`BeanCreationException` for primitives.
+    - Spring `@Value` (referenced via FQN reflection helper in the test, not a 
compile-time dep) honored identically.
+    - `@Value` + `@Inject` on the same site rejected with 
`BeanCreationException`.
+
+### Phase 2 — `${xxx}` shortcut in VarResolver (juneau-commons)
+
+- In 
`juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/svl/VarResolverSession.java`,
 the tokenizer scanning for `$X{...}`: add a single special case — when the 
scanner sees `$` immediately followed by `{`, treat the segment as if it were 
`$P{...}`. No new `Var` class needed.
+- Nested resolution (`${foo.${env}.url}`) falls out for free because 
`VarResolver` already recursively resolves inner vars before passing the key to 
the outer var.
+- Acceptance tests in 
`juneau-utest/src/test/java/org/apache/juneau/commons/svl/DollarBraceShortcut_Test.java`:
+    - `${foo}` resolves identically to `$P{foo}`.
+    - `${foo:bar}` returns `"bar"` when `foo` is unset (uses `DefaultingVar` 
semantics).
+    - `${foo.${env}.url}` resolves nested.
+    - Literal `$P{...}`, `$C{...}`, `$E{...}`, `$IF{...}` still work — no 
regression. (Re-run the full SVL test suite.)
+    - Escape semantics (`\${literal}`) still pass through unchanged.
+
+### Phase 3 — Auto-register Config as a PropertySource (juneau-config + 
juneau-microservice)
+
+- Add 
`juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigPropertySourceProvider.java`.
+    - Strategy: returns `new 
ConfigPropertySource(Config.create().name("juneau.cfg").build())` if a 
discoverable `juneau.cfg` exists on the classpath; otherwise returns `null` 
(the SPI already filters nulls).
+    - `order()` returns something sensibly low (e.g. `100`) so user-supplied 
providers can add themselves "after" by returning a higher number — 
`Settings`'s "sources walked in reverse insertion order" means higher-`order()` 
providers register last and therefore win.
+- Add 
`juneau-core/juneau-config/src/main/resources/META-INF/services/org.apache.juneau.commons.settings.PropertySourceProvider`
 containing the FQN of `ConfigPropertySourceProvider`.
+- In 
`juneau-microservice/juneau-microservice/src/main/java/org/apache/juneau/microservice/Microservice.java`'s
 build path (the spot where `Config` is resolved into the bean store): also 
call `Settings.get().addSource(new ConfigPropertySource(cfg))` so the 
per-microservice `Config` shadows the auto-registered classpath default.
+- Same hook in `RestContext.Builder` so non-microservice REST resources that 
build their own `Config` get the same treatment.
+- Acceptance tests:
+    - Classpath-only path: drop a `juneau.cfg` with `[s]/k=v` into 
`juneau-utest`'s test resources, instantiate a `@Value("${s/k}") String v` bean 
**without** a microservice. Expect `"v"`.
+    - Microservice-override path: build a `Microservice` with 
`Config.create().memStore().build()` having `[s]/k=micro`; same bean should 
resolve to `"micro"` rather than the classpath value.
+
+### Phase 4 — Spring Environment bridge (juneau-rest-server-springboot)
+
+- Add 
`juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringEnvironmentPropertySource.java`.
 Wraps `org.springframework.core.env.Environment`. `get(name)` returns 
`PropertyLookupResult.present(opt(env.getProperty(name)))` if 
`env.containsProperty(name)`, otherwise `PropertyLookupResult.missing()`.
+- Auto-registration: in the existing Spring `@Configuration` in 
`juneau-rest-server-springboot` (or via `SpringBeanStore`'s constructor — the 
place where `ApplicationContext` is captured), call 
`Settings.get().addSource(new 
SpringEnvironmentPropertySource(appContext.getEnvironment()))`. Adding it from 
Spring guarantees `application.yaml` / `application.properties` / 
`--db.url=...` / profiles all participate via Spring's standard resolution 
rules.
+- Acceptance test: a `@SpringBootTest` mirroring 
[SpringBeanStore_Test.java](../juneau-utest/src/test/java/org/apache/juneau/rest/springboot/SpringBeanStore_Test.java)
 puts `db.url=spring-value` in `application.yaml` (or `@TestPropertySource`), 
defines a Juneau `@Rest` resource whose constructor takes `@Value("${db.url}") 
String url`, asserts the field is `"spring-value"`. Also verify Spring 
`@Value("${db.url}")` (FQN-imported) resolves identically.
+
+### Phase 5 — Docs + release notes
+
+- Release-notes entry under `juneau-docs/pages/release-notes/9.5.0.md`:
+    - **Top-level major change**: "New `@Value` annotation + `${...}` shortcut 
+ Spring Boot `application.yaml` bridge".
+    - Module-level entries under `juneau-marshall` (the `${...}` shortcut) and 
`juneau-rest-server` / `juneau-rest-server-springboot` (the `@Value` + Spring 
bridge).
+- New topic page `juneau-docs/pages/topics/...ValueAnnotationBasics.md` 
covering:
+    - `@Value` annotation syntax + supported target types.
+    - `${...}` shortcut + nesting + default-value syntax.
+    - Resolution order (thread-local → global → sources reverse-insertion → 
sys-props → env).
+    - Spring Boot bridge — what works automatically, what doesn't.
+- Cross-link from existing `SimpleVariableLanguageBasics` and `VariableBasics` 
topic pages.
+
+## Risk notes
+
+- The `${...}` tokenizer change is the only place in the plan that touches a 
hot path (every `VarResolver.resolve(...)` call). Mitigation: keep the change 
in `VarResolverSession.parse(...)` to a single 3-line lookahead (`$` followed 
by `{` → emit `P` as the var name), and run the full existing VarResolver test 
suite before any other change in Phase 2.
+- `ConfigPropertySourceProvider`'s classpath-default `juneau.cfg` lookup must 
be **silent** when the file is absent (return `null`, no warning log). Many 
deployments will not want a classpath config and shouldn't see noise.
+- Spring `Environment.getProperty(...)` can be slow on cold lookups for 
unknown keys (it walks every nested `PropertySource`). `Settings` already 
memoizes through `StringSetting` so this only bites the first lookup per key 
per session.
+- `Settings.get()` is a process-wide singleton. Tests that mutate it via 
`addSource(...)` must clean up in `@AfterEach` to avoid cross-test bleed. 
Pattern: capture the source returned from `addSource(...)`, hold it in a test 
field, remove it in teardown. (May need to add a 
`Settings.removeSource(PropertySource)` if one doesn't exist — verify during 
Phase 3.)
+- Eager scanning of `@Value` fields on cold-start: `BeanInstantiator` already 
scans annotations once per class; adding `@Value` to its set of recognized 
annotations doesn't change the asymptotic cost.
+
+## Out of scope / follow-on TODOs
+
+- **`#{...}` SpEL / Jakarta-EL expression support.** Recommended path when it 
lands: separate `juneau-config-el` Maven module declaring `jakarta.el` in 
`provided` scope, registering a `$EL{...}` var that the `#{...}` shortcut 
lowers to. Track as its own TODO post-9.5 if demand surfaces.
+- **`@ConfigurationProperties`-style prefix binding** 
(`@ConfigurationProperties(prefix="app.db") DbConfig dbConfig`). `@Value` is 
single-key; prefix binding is a separate effort.
+- **Hot-reload-aware `@Value` fields** that automatically re-resolve when the 
backing `Config` fires a `ConfigEvent`. Spring's `@Value` is also single-shot 
at injection time; users who need hot reload should observe `Config` directly.
diff --git a/todo/TODO.md b/todo/TODO.md
index f192264826..31832f67cb 100644
--- a/todo/TODO.md
+++ b/todo/TODO.md
@@ -1,8 +1,8 @@
 # TODO
 
-## Execution order (re-evaluated 2026-05-24)
+## Execution order (re-evaluated 2026-05-25)
 
-Four foundational TODOs (TODO-73, TODO-81, TODO-69) and four mixin packs 
(TODO-74–77) have landed; next up per execution order is TODO-78 (JSP view 
module). Plans for TODO-71, TODO-82–84, TODO-85–87, and TODO-89 are now fleshed 
out (2026-05-24). TODO-20 (rest debug rethink) and TODO-37 (agent instruction 
consolidation) remain parked.
+Four foundational TODOs (TODO-73, TODO-81, TODO-69) and four mixin packs 
(TODO-74–77) have landed. **TODO-20 (rest debug rethink) was un-parked 
2026-05-25** with a hard-break decision + source-of-truth nested-annotation 
pattern added to the plan; it is now the next-in-flight item ahead of TODO-78. 
Plans for TODO-71, TODO-82–84, TODO-85–87, and TODO-89 are fleshed out 
(2026-05-24). TODO-37 (agent instruction consolidation) remains parked.
 
 ### Completed foundations + mixin family
 
@@ -19,49 +19,52 @@ Four foundational TODOs (TODO-73, TODO-81, TODO-69) and 
four mixin packs (TODO-7
 
 7. ~~**TODO-69** — AuthN guards + isolated `juneau-rest-server-jwt` 
sub-module.~~ ✅ done — see `todo/FINISHED-69-authn-guards-jwt-apikey.md`.
 
-**Phase B — view infrastructure (hard prereq for Phase C):**
+**Phase B — debug rethink (next in flight):**
 
-8. **TODO-78** — JSP module (`juneau-rest-server-view-jsp`). Introduces the 
generic `View` interface in core `juneau-rest-server` and the 
`ResponseProcessor`-based renderer pattern that TODO-82/83/84 build on. 
Engine-agnostic POM (Option B) stance baked in. HARD prereq for TODO-82/83/84.
+8. **TODO-20** — Rest debug rethink. Collapses five `@Rest`/`@RestOp` debug 
attributes + `DebugEnablement` + parallel `CallLogger` rule lists into a single 
`DebugConfig` bean + a typed `@Debug` annotation. **Hard break** (no 
deprecation cycle; migration notes in 
`juneau-docs/pages/topics/23.01.V9.5-migration-guide.md`). **Source-of-truth 
pattern** — `@Debug` is nested as `@Rest(debug=@Debug(...))` and 
`@RestOp(debug=@Debug(...))` so the `@Rest`/`@RestOp` annotation is the 
canonical capab [...]
 
-**Phase C — view module siblings (parallelizable after TODO-78 lands; no 
inter-sibling deps):**
+**Phase C — view infrastructure (hard prereq for Phase D):**
 
-9. **TODO-82** — Thymeleaf view module. Highest priority of the three (Spring 
Boot's default web view; biggest existing-user-base migration path). Plan: 
`todo/TODO-82-view-module-thymeleaf.md`.
-10. **TODO-83** — Mustache view module. Smallest surface area, lowest risk. 
Plan: `todo/TODO-83-view-module-mustache.md`.
-11. **TODO-84** — FreeMarker view module. Apache-family alignment bonus. Plan: 
`todo/TODO-84-view-module-freemarker.md`.
+9. **TODO-78** — JSP module (`juneau-rest-server-view-jsp`). Introduces the 
generic `View` interface in core `juneau-rest-server` and the 
`ResponseProcessor`-based renderer pattern that TODO-82/83/84 build on. 
Engine-agnostic POM (Option B) stance baked in. HARD prereq for TODO-82/83/84.
 
-**Phase D — application showcase tier:**
+**Phase D — view module siblings (parallelizable after TODO-78 lands; no 
inter-sibling deps):**
 
-12. **TODO-85** — `juneau-microservice-jetty-starter`. Standalone, no hard 
deps; can land any time after FINISHED-74/75/76/77 (which it bundles). Plan: 
`todo/TODO-85-microservice-jetty-starter.md`.
-13. **TODO-86** — `juneau-petstore-jetty` sample app. Soft deps on TODO-85 
(starter fallback to raw `juneau-microservice-jetty`), TODO-69 (auth fallback 
to `DenyAllGuard`), TODO-82 (preferred view engine: Thymeleaf, fallback to 
TODO-78 JSP). Defines the shared `juneau-petstore-core` module consumed by 
TODO-87. Plan: `todo/TODO-86-petstore-jetty-app.md`.
-14. **TODO-87** — `juneau-petstore-springboot` sample app. HARD dep on 
`juneau-petstore-core` from TODO-86; should land alongside or immediately after 
TODO-86 as a coherent landing. Plan: `todo/TODO-87-petstore-springboot-app.md`.
+10. **TODO-82** — Thymeleaf view module. Highest priority of the three (Spring 
Boot's default web view; biggest existing-user-base migration path). Plan: 
`todo/TODO-82-view-module-thymeleaf.md`.
+11. **TODO-83** — Mustache view module. Smallest surface area, lowest risk. 
Plan: `todo/TODO-83-view-module-mustache.md`.
+12. **TODO-84** — FreeMarker view module. Apache-family alignment bonus. Plan: 
`todo/TODO-84-view-module-freemarker.md`.
+
+**Phase E — application showcase tier:**
+
+13. **TODO-85** — `juneau-microservice-jetty-starter`. Standalone, no hard 
deps; can land any time after FINISHED-74/75/76/77 (which it bundles). Plan: 
`todo/TODO-85-microservice-jetty-starter.md`.
+14. **TODO-86** — `juneau-petstore-jetty` sample app. Soft deps on TODO-85 
(starter fallback to raw `juneau-microservice-jetty`), TODO-69 (auth fallback 
to `DenyAllGuard`), TODO-82 (preferred view engine: Thymeleaf, fallback to 
TODO-78 JSP). Defines the shared `juneau-petstore-core` module consumed by 
TODO-87. Plan: `todo/TODO-86-petstore-jetty-app.md`.
+15. **TODO-87** — `juneau-petstore-springboot` sample app. HARD dep on 
`juneau-petstore-core` from TODO-86; should land alongside or immediately after 
TODO-86 as a coherent landing. Plan: `todo/TODO-87-petstore-springboot-app.md`.
 
 > **Open question for TODO-86/87** (surfaced by planning worker): factor REST 
 > resource classes into a third sibling `juneau-petstore-rest` module so both 
 > sample apps consume identical resource classes? Worth resolving before 
 > either starts implementation.
 
-**Phase E — quality-of-life follow-ons (parallelizable, any order, anytime 
after Phase A):**
+**Phase F — quality-of-life follow-ons (parallelizable, any order, anytime 
after Phase A):**
 
-15. **TODO-89** — `RateLimitGuard.Storage.snapshot()` SPI + 
`BasicAdminResource` enrichment. Small (~30 LOC + 2 test files), closes a known 
carry-over from FINISHED-77 (`"buckets": []` placeholder). Plan: 
`todo/TODO-89-ratelimit-storage-snapshot-spi.md`.
-16. **TODO-71** — Doc-site script + Docusaurus search swap. Mostly already 
done (planning worker found `juneau-docs/scripts/build-docs.py` + 
`.github/workflows/deploy-docs.yml.disabled` already in place); this TODO is 
now a cutover/cleanup + the Algolia → `@easyops-cn/docusaurus-search-local` 
swap. Plan: `todo/TODO-71-docs-site-script-search-swap.md`.
-17. **TODO-88** — YAML parser buffer-underflow on large OpenAPI 3.1 documents. 
Latent parser bug in `juneau-marshall`; opportunistic. Plan file TBD.
+16. **TODO-89** — `RateLimitGuard.Storage.snapshot()` SPI + 
`BasicAdminResource` enrichment. Small (~30 LOC + 2 test files), closes a known 
carry-over from FINISHED-77 (`"buckets": []` placeholder). Plan: 
`todo/TODO-89-ratelimit-storage-snapshot-spi.md`.
+17. **TODO-71** — Doc-site script + Docusaurus search swap. Mostly already 
done (planning worker found `juneau-docs/scripts/build-docs.py` + 
`.github/workflows/deploy-docs.yml.disabled` already in place); this TODO is 
now a cutover/cleanup + the Algolia → `@easyops-cn/docusaurus-search-local` 
swap. Plan: `todo/TODO-71-docs-site-script-search-swap.md`.
+18. **TODO-88** — YAML parser buffer-underflow on large OpenAPI 3.1 documents. 
Latent parser bug in `juneau-marshall`; opportunistic. Plan file TBD.
 
-**Phase F — server-feature track (independent; can interleave anywhere after 
Phase A):**
+**Phase G — server-feature track (independent; can interleave anywhere after 
Phase A):**
 
-18. **TODO-67** — Observability (Micrometer + OpenTelemetry). Plan: 
`todo/TODO-67-observability-micrometer-otel.md`.
-19. **TODO-68** — Bean Validation (Jakarta Validation 3.x). Plan: 
`todo/TODO-68-bean-validation-integration.md`.
-20. **TODO-70** — `CompletableFuture<?>` return-type + virtual-thread 
per-request dispatch. Plan: 
`todo/TODO-70-async-completablefuture-virtual-threads.md`.
-21. **TODO-79** — Juneau `@Value` annotation + Spring Boot `application.yaml` 
bridge for the Config API. Plan file TBD.
+19. **TODO-67** — Observability (Micrometer + OpenTelemetry). Plan: 
`todo/TODO-67-observability-micrometer-otel.md`.
+20. **TODO-68** — Bean Validation (Jakarta Validation 3.x). Plan: 
`todo/TODO-68-bean-validation-integration.md`.
+21. **TODO-70** — `CompletableFuture<?>` return-type + virtual-thread 
per-request dispatch. Plan: 
`todo/TODO-70-async-completablefuture-virtual-threads.md`.
+22. **TODO-79** — Juneau `@Value` annotation + Spring Boot `application.yaml` 
bridge for the Config API. Plan: 
`todo/TODO-79-value-annotation-config-bridge.md`.
 
 ### Parked / unscheduled
 
-- **TODO-20** — Rest debug rethink (parked pending user review).
 - **TODO-37** — Agent instruction consolidation (unscoped).
 
 ### Natural review seams
 
-Foundations (TODO-73 + TODO-81 + TODO-69) → mixin family (TODO-74–77) → view 
infrastructure + siblings (TODO-78 + TODO-82/83/84) → application showcase 
(TODO-85/86/87) → quality-of-life (TODO-89 + TODO-71 + TODO-88) → 
server-feature track (TODO-67 + TODO-68 + TODO-70 + TODO-79).
+Foundations (TODO-73 + TODO-81 + TODO-69) → mixin family (TODO-74–77) → debug 
rethink (TODO-20) → view infrastructure + siblings (TODO-78 + TODO-82/83/84) → 
application showcase (TODO-85/86/87) → quality-of-life (TODO-89 + TODO-71 + 
TODO-88) → server-feature track (TODO-67 + TODO-68 + TODO-70 + TODO-79).
 
 ## Items
 
-- [TODO-20] - Rest debug rethink.
+- [TODO-20] Rest debug rethink — collapses `DebugEnablement` + `CallLogger` 
rule lists + five `@Rest`/`@RestOp` debug attributes into a single 
`DebugConfig` bean + typed `@Debug` annotation. Hard break in 9.5 (no 
deprecation cycle) with migration notes. Source-of-truth pattern — `@Debug` is 
nested as `@Rest(debug=@Debug(...))` and `@RestOp(debug=@Debug(...))` (primary 
placement) with standalone `@Debug` retained as an escape hatch. See 
`todo/TODO-20-rest-debug-rethink.md`.
 
 - [TODO-37] - Agent instruction consolidation.
 
@@ -75,8 +78,6 @@ Foundations (TODO-73 + TODO-81 + TODO-69) → mixin family 
(TODO-74–77) → vi
 
 - [TODO-78] JSP servlet support module (`juneau-rest-server-view-jsp`) — new 
module shipping `BasicJspResource` mixin + `JspViewRenderer`; isolates Apache 
Jasper / `jakarta.servlet.jsp.*` / JSTL deps from core. See 
`todo/TODO-78-mixin-jsp-module.md`.
 
-- [TODO-79] Juneau `@Value` annotation + Spring Boot `application.yaml` bridge 
for the Config API — introduce a `@Value("${...}")` annotation on top of 
`Config` so beans / fields / setters can read configuration values 
declaratively (analog to Spring's `@Value`); add a Spring Boot integration so 
values defined in `application.yaml` / `application.properties` are accessible 
through the Juneau `Config` API uniformly with native `*.cfg` files. Plan file 
TBD.
-
 - [TODO-82] Thymeleaf view module (`juneau-rest-server-view-thymeleaf`) — 
sibling to TODO-78's JSP module. New Maven module shipping 
`BasicThymeleafResource` mixin + `ThymeleafViewRenderer` + `ThymeleafView` impl 
of the `View` interface introduced by TODO-78. High priority since Thymeleaf is 
Spring Boot's default web view technology; large existing user base for 
Spring-Boot-on-Juneau migrations. Same engine-agnostic POM stance as TODO-78 
(Option B): bridge module declares Thymeleaf core  [...]
 
 - [TODO-83] Mustache view module (`juneau-rest-server-view-mustache`) — 
sibling to TODO-78's JSP module. New Maven module shipping 
`BasicMustacheResource` mixin + `MustacheViewRenderer` + `MustacheView` impl of 
the `View` interface introduced by TODO-78. Logic-less templates; common choice 
for content authored by non-Java developers. Same engine-agnostic POM stance as 
TODO-78 (Option B): bridge declares the Mustache API in `provided` scope; 
example supplies the concrete impl (resolved de [...]

Reply via email to