Codename: problem-report

Problem 48 browser host-context parsing (2026-10-18): NYTProf attributed about
14.7 ms across 703 calls to host-normalization substitutions in
`Web::App::_top_context_html`. Characterization tests preserve existing URL
semantics; fixed-prefix checks and one `index` lookup measured 1.32x faster over
nine alternating Docker samples with exact output parity. The saved-page route
response remained byte-identical, so no route-level gain is claimed. Problem
48's all-subroutine 50x objective remains open.

Problem 48 request snapshot follow-up (2026-10-18): an environment-signature
Memoize-style candidate was compared with the exact existing JSON implementation
in nine alternating Docker runs of 50,000 calls. The candidate measured 0.85x
(slower) with identical signatures and was removed. A separate red-first test
found stale prompt-summary HTML when `/system/status` synchronized changed
collector indicators before the next page render. Collector synchronization
now invalidates cached summary text; the Dancer route and status-cache tests
pass. The combined focused Docker rerun passed 1,101 assertions. The warmed
route measured 0.362170 ms/request afterward with exact 19,341-byte response
parity; timing variation is not claimed as a speedup. The subsequent full
Docker coverage gate passed all 285 files and 25,293 tests with 100.0%
statement, branch, condition, and subroutine coverage across `lib/` (51,645
detail rows; no stale uncoverable annotations). Optional browser, Windows QEMU,
CPANTS, POD, and audit-tool checks were skipped because their external
requirements were unavailable. The universal 50x objective remains open.

Problem 48 release artifact hygiene (2026-10-18): the 6.00 tarball included
`cover_db-p48-focus`, generated by a named Devel::Cover run. The release
exclusion covered `cover_db` and underscore suffixes but not hyphen suffixes.
Red-first metadata assertions reproduced the missing pattern and archive leak.
Gather, prune, and manifest-skip rules now recognize both suffix forms, and
the release metadata test checks archive members. The rebuilt 6.00 archive has
no matching paths; `t/15-release-metadata.t` passes 5,756 assertions, and the
blank-container cpanm installation plus integration run passes.

Problem 48 optional watcher benchmark portability (2026-10-18): the 5.99
blank Docker install reproduced two additional failures when FFI::Platypus was
not installed: fixed expectations for one nav scan and six containment checks.
The runtime correctly preserved response bytes but used the portable path
(nine scans, 36 checks). The route benchmark now reports native backend state
and actual snapshot hits and supports `--no-native-watch`; tests require byte
parity in either mode and assert native optimization counts only when a watcher
exists. Combined focused Docker tests pass 432 assertions. Version 6.00 tarball
installation and the full blank-container integration run pass; Problem 48
stays open.

Problem 48 lazy watcher module after chdir (2026-10-18): the security gate
reproduced HTTP 500 for skill bookmark pages because the app lazily required
the optional watcher through a relative `@INC` after request code had changed
the process working directory. App and dispatcher now load it from their
captured absolute source paths while preserving an existing `%INC` entry.
Focused Docker verification passes 722 assertions across the Dancer route,
dispatcher, and benchmark tests. The 6.00 blank-container install and
integration run pass.

Problem 48 blank-tarball test portability (2026-10-18): a Docker `cpanm`
install/test reproduced two failures: a direct skill-dispatcher test depended
on optional FFI::Platypus being installed, and route-control tests used a
repository-specific fixed response hash that differed from the extracted
distribution path. Injected the deterministic watcher and changed parity
assertions to use the hash from the default benchmark run. Focused Docker tests
pass 425 assertions. The follow-up 5.99 run then revealed native-cache count
assumptions, addressed in 6.00; the 6.00 blank tarball install and integration
run pass.
Problem 48 remains open.

Problem 48 native dense PageDocument escaping (2026-10-17): expected exact
legacy HTML bytes/UTF-8 flags and faster dense long text without regressing
ordinary page text. NYTProf identified PageDocument::_html's repeated
substitutions; a red-first Docker test failed until the dense path delegated to
the shared XS escaper. Current dispatch uses XS only for scalar text >=64
characters with at least four body metacharacters in the first 16; short and
sparse text keeps the Perl path and quote behavior. Focused fallback and
compiled-XS Docker tests passed. Seven alternating samples measured 11.29x for
384-character dense input, 1.09x for a short plain value, 1.06x for long plain
text, and 0.94x for short quote-heavy input (near parity). The source-only
fallback measured 1.08x for short plain text, 0.85x for short quote-heavy
text, 1.11x for long plain text, and 1.34x for dense input. Output bytes and
UTF-8 flags stayed identical. This is not a route-level or per-subroutine 50x
claim; Problem 48 remains open.
The native object compiled and loaded from blib/arch, but the following MakeMaker
make failed during script fixin because /root/perl5's Storable binary does not
match system Perl 5.40.1. This remains an unresolved build-environment gate.

This file is the chronological evidence log; `doc/problem-report.md` is the
current numbered status index. Historical status snapshots below are retained
as historical evidence and are superseded by later release entries. For
example, the 2026-10-03 verification snapshot recorded 257 test files and
22,371 tests passing; it first measured 99.9% condition coverage, then reached
100.0% on all four metrics after a test-only addition. At that point the full
suite had not been rerun with that addition, and Problems 12, 32, and 34 still
had release delivery in progress. Later entries record subsequent verification
and release outcomes; do not treat this opening snapshot as the current status.

Earlier release references below are from `Changes`. The original
red-test transcripts were not retained, so a detached pre-fix snapshot was
reconstructed for the 5.00 dashboard/skill batch: commit `37a5bd77` (parent of
`2a09c53b`), with the regression-test changes from `2a09c53b` applied, ran in
the project `dev` container. This verifies pre-fix failures independently of
the current-green full suite, but is not a substitute for the original logs.
For Problems 12–14, pre-fix commit `2a09c53b` was tested with assertions from
`7b0974e6` applied in a separate container worktree. Problems 15 and 16 were
reconstructed from `f1bdabe1` (parent of `b5e7ac4a`) and `e8d5d54f` (parent of
`11aaaa1f`) respectively; Problems 17–18 from `76df976d` (parent of
`6dcbfe35`); Problem 19 from `6dcbfe35` (parent of `874d8df6`).

Pre-fix reconstruction results for Problems 1–11:
- Red reproduced: Problem 1 (`t/03-web-app.t`, `t/104-skilldispatcher-coverage.t`: saved URL 404/no redirect or merged query); Problem 2 (`t/08-web-update-coverage.t`: skill INCLUDE not found); Problem 3 (`t/85-zipper-coverage.t`: `[% args %]` remained literal); Problem 5 (`t/104`, `t/87`: skill/CLI env layer absent or precedence wrong); Problem 6 (`t/104`: skill library module not found); Problem 7 (`t/39`: completion exposed `foo.__init__`); Problem 9 (`t/69`: `ticket` still completed as workspace).
- Not red at the 4.90 snapshot: Problem 4 already staged helpers privately there; the underlying flat-path defect was reproduced at the pre-fix 1.97 snapshot (see below). Problem 8's cwd assertions were masked by an earlier unrelated missing CLI-layer method, then reproduced in an isolated focused run (see below). Problem 10's valid four-segment nested command passed before and after the 5.00 batch; the supplied directory listing omits the `c` skill directory implied by command `a.b.c.d`, so the literal example is inconsistent and is not counted as a reproduced bug. Problem 11's cwd-overrides-skill assertions did fail in `t/05` and `t/19`.
- Current verification: the same named regression tests passed in the full current suite (204 files, 19,197 tests). The full suite did not run the separate Problem 20 aggregate coverage gate.
- Problems 12–14 red reproduced: `t/91` failed to resolve the skill workspace alias; `t/76` and `t/99` failed the automatic DataHelper import assertions; `t/76` showed skill Dashboard routes bypassing authorization and not loading into Dancer2.
- Problem 15 red reproduced in `t/94-dockercompose-coverage.t`: base/overlay selection assertions failed and the development enable method was absent.
- Problem 16 red reproduced in `t/90-cli-paths-coverage.t`: the Folder.pm alias returned no target and could not be listed through the path registry.
- Problems 17–18 red reproduced: `t/102-skillmanager-coverage.t` reported present `ddfile`/`ddfile.local` manifests as missing; `t/72-pagedocument-coverage.t` failed HEAD parsing, serialization, and rendered-head assertions.
- Problem 19 red reproduced in `t/76-web-dancerapp-coverage.t`: after isolating the new helper-unit block that aborts before integration assertions on old code, both `/app/...` and `/ajax/...` returned only the default CSP instead of the skill hook's `unsafe-eval` policy. The variable, startup load, and once-only expectations did not fail in that run.

Problem 1: Forward saved bookmark URLs and merge request query parameters (2a09c53b)
(4.92, 4.94–4.95; t/03-web-app.t, t/104-skilldispatcher-coverage.t, t/131-bookmark-url-encoding.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: redirect saved external URLs while preserving saved parameters, overriding matching keys from the request, appending new keys, and honoring selected array values.
Root cause: saved URL files were passed through the instruction-page parser and the legacy forwarding path did not merge request keys over the saved query.

Problem 2: Resolve skill-aware Template Toolkit includes (2a09c53b)
(4.92; t/08-web-update-coverage.t, t/20-skill-web-routes.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: skill bookmark templates include both local dashboard fragments and explicit skill dashboard paths.
Root cause: the template include allow-list contained runtime/dashboard roots but omitted the owning skill's dashboard root and explicit `skills/<name>/dashboards/...` form.

Problem 3: Render supplied data in saved Ajax code templates (2a09c53b)
(4.92; t/12-legacy-helper-coverage.t, t/85-zipper-coverage.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: process the code template with its data before storing/executing the generated handler.
Root cause: saved Ajax handlers persisted the raw `code` source instead of rendering it with the supplied Template Toolkit `data` first.

Problem 4: Keep dashboard-owned init helpers private (f561a7e0; regression guard 2a09c53b)
(1.98 implementation, 4.92 regression test; t/30-dashboard-loader.t)
(status: historical red/current green)
Expected: init stages built-in helpers only under `cli/dd` and preserves user files in `cli`.
Root cause: the helper destination was the shared user `cli/` root rather than the reserved `cli/dd/` namespace.
Historical red/fix check: at pre-fix commit `c65a3d21` (1.97), the relevant helper staging call reported `cli/dd/jq=0 cli/jq=1`; at current source in the Docker `dev` container it reported `cli/dd/jq=1 cli/jq=0`. The broad legacy `t/30` attempt at 1.97 also encountered missing `TOML::Tiny` and an injected lazy-loader sentinel before reaching clean init, so those unrelated failures are excluded from the reproduction claim. Current `t/30-dashboard-loader.t` passed in Docker.

Problem 5: Inherit nested skill CLI environment and runtime layers (2a09c53b)
(4.92; t/19-skill-system.t, t/87-envloader-coverage.t, t/104-skilldispatcher-coverage.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: parent-to-leaf skill `.env`/`.env.pl` and DD-OOP runtime layers reach the leaf command.
Root cause: dispatch built its environment from the command-provider layer only and omitted ancestor skill and skill-CLI env files.

Problem 6: Add the owning skill `lib/` to `@INC` for CLI and dashboard CODE (2a09c53b, bc516b29)
(4.97, 5.06; t/20-skill-web-routes.t, t/76-web-dancerapp-coverage.t, t/104-skilldispatcher-coverage.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: the executing skill's library is first in `@INC` for both runtimes.
Root cause: runtime setup prepended application libraries but never added the active skill's `lib/` directory to CLI `PERL5LIB` or dashboard CODE `@INC`.

Problem 7: Complete `cli/__init__` by the bare skill name (2a09c53b)
(4.97; t/39-cli-suggest-complete-coverage.t, t/104-skilldispatcher-coverage.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: completion shows `foo`, not `foo.__init__`.
Root cause: completion treated the internal `__init__` entrypoint as a literal command token rather than the skill's default command.
Reopened 2026-10-01 for nested dispatch. Docker reproduction from `/tmp/capture-297.txt`: create `~/.developer-dashboard/skills/foo/skills/bar/cli/__init__.sh`, then `d2 foo.bar`; the file runs directly but Tira reports `Command 'bar' not found in skill 'foo'`. Red tests in `t/104-skilldispatcher-coverage.t` reproduced the missing one-level lookup and showed that a parent initializer incorrectly shadowed a deeper initializer. Root cause: `_command_root_specs` omitted a terminal nested-skill initializer candidate, and `_command_spec` attempted each candidate's initializer fallback before checking deeper candidates. Fix: check all explicit commands first, then initializer fallbacks deepest-to-root; allow the complete dotted tail to identify a nested skill's `cli/__init__`. Terminal and prefix candidates are validated with `validated_path_segments` before being joined, so the new lookup does not turn command text into traversing paths. Verified the exact capture shape and one deeper level in Docker, including argument forwarding, explicit-command precedence, and rejected traversal input. Security review: resolved commands still execute as an argument vector (no shell); this change adds no dependency or external input channel. (5.36)
(status: complete; original fix ref 2a09c53b, nested-dispatch follow-up delivered on master)

Problem 8: Load invocation-directory env files outside home/project roots (2a09c53b)
(4.97; t/06-env-overrides.t, t/87-envloader-coverage.t, t/79-perlenv-coverage.t)
(status: pre-fix red reproduced in Docker; current regression verified)
Expected: `d2` loads the current directory's `.env`/`.env.pl` independently of home/project ancestry.
Root cause: `_plain_directory_layers` returned no layers unless cwd was beneath the home or detected project root.
Pre-fix reproduction: at detached snapshot `37a5bd77`, applied only the P8 regression assertions and ran `d2 docker compose exec dev prove -l t/87-envloader-coverage.t`; tests 78–79 failed because `_plain_directory_layers` returned no invocation-cwd layer (`/x/y/z`) when outside home/project roots. The unrelated missing CLI-layer assertions were omitted from this focused scratch run so they could not abort before P8 was reached.

Problem 9: Remove the confusing `d2 ticket` alias (2a09c53b)
(4.97; t/39-cli-suggest-complete-coverage.t, t/69-cli-complete-coverage.t, t/91-cli-ticket-coverage.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: use `d2 workspace`; `ticket` is not exposed as a competing command.
Root cause: the compatibility alias and staged helper still exposed `ticket` alongside the canonical `workspace` command.

Problem 10: Resolve arbitrarily nested skill CLI names (no implementation commit identified; regression guard 2a09c53b)
(4.97 test record; t/104-skilldispatcher-coverage.t, t/19-skill-system.t)
(status: current valid nested-command regression verified; historical reported path remains ambiguous)
Expected: a command such as `a.b.c.d` resolves through each nested `skills/` directory.
Reproduction audit: the Docker `dev` test `t/104-skilldispatcher-coverage.t` includes an equivalent four-segment route (`runner.child.grand.deep`) and passed at both pre-fix-batch commit `37a5bd77` and current source. The supplied path `a/skills/b/skills/cli/d.pl` contains no `c` directory, although command `a.b.c.d` requires `c` as a nested skill; with the intended `a/skills/b/skills/c/cli/d.pl` shape, the equivalent regression test resolves. Therefore no root-cause/fix cycle is attributed to this report until the exact missing directory or intended command is clarified; the existing dispatcher behavior is tested and working.

Problem 11: Let invocation cwd env override skill defaults (2a09c53b)
(4.97; t/20-skill-web-routes.t, t/06-env-overrides.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: apply cwd `.env` last, so its values override inherited skill values.
Root cause: the dispatcher loaded the skill `.env` after runtime/cwd env, giving skill defaults the final overwrite.

Problem 12: Resolve skill-qualified workspace aliases (7b0974e6)
(5.01; t/91-cli-ticket-coverage.t, t/205-skill-depth-alias.t)
(status: original behavior verified; Folder.pm `-c` regression fixed in 5.42)
Expected: `d2 workspace bar.foo -c` resolves configured and listed skill `Folder.pm` aliases, changes into that directory, and starts the tmux session there. Dotted aliases must work for top-level, nested, and deeper installed skills.
Root cause: the earlier workspace change handled config-registered aliases but `registered_workspace_dir` did not consult the shared skill Folder.pm alias resolver. The workspace alias without `-c` remains supported.

Problem 13: Import DataHelper in saved-page CODE automatically (7b0974e6)
(5.01; t/76-web-dancerapp-coverage.t, t/99-pageruntime-coverage.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: CODE blocks can use DataHelper functions without repeating an import.
Root cause: CODE evaluation did not install the standard `Developer::Dashboard::DataHelper` imports into each execution package.

Problem 14: Load skill Dashboard extensions at web startup (7b0974e6; refined 874d8df6)
(5.01, refined by 5.18; t/20-skill-web-routes.t, t/30-dashboard-loader.t, t/76-web-dancerapp-coverage.t)
(status: pre-fix red reconstructed; current regression verified; see Problem 19 for hook behavior)
Expected: active skills' `lib/Dashboard.pm` routes/settings join the Dancer app at startup behind dashboard authorization.
Root cause: startup composition did not discover and load active skill extensions into the shared Dancer2 application and apply its authorization gate.

Problem 15: Support opt-in Docker development compose overlays (b5e7ac4a)
(5.07; t/05-cli-smoke.t, t/10-extension-action-docker.t, t/94-dockercompose-coverage.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: base `compose.yml` always loads; `development.compose.yml` overlays only when enabled; disable markers exclude a service.
Root cause: the resolver lacked a persistent development opt-in marker and did not layer the base and development files by service state.

Problem 16: Merge Folder.pm aliases into path lookup and completion (11aaaa1f)
(5.13–5.14; t/90-cli-paths-coverage.t, t/97-pathregistry-coverage.t, t/205-skill-depth-alias.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: skill Folder methods and `__list__` supply read-only aliases while configured aliases remain writable and take precedence.
Root cause: `d2 paths`/`cdr` and completion only merged JSON-configured aliases, ignoring the active skill's `Folder.pm` providers.

Problem 17: Report present-but-empty skill dependency manifests accurately (6dcbfe35)
(5.16; t/19-skill-system.t, t/102-skillmanager-coverage.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: say “no dependency installs needed” for an existing empty/effectively empty manifest; say “missing” only when absent.
Root cause: manifest detection conflated a present zero-length file with a missing file.

Problem 18: Inject bookmark HEAD content into the document head (6dcbfe35)
(5.17; t/72-pagedocument-coverage.t)
(status: pre-fix red reconstructed; current regression verified)
Expected: parse, serialize, and render the bookmark's trusted `HEAD:` content inside `<head>`.
Root cause: the page-document section parser and renderer had no `HEAD` field or serialization path.

Problem 19: Preserve skill Dancer hooks, variables, and startup loading (874d8df6)
(5.18; t/20-skill-web-routes.t, t/30-dashboard-loader.t, t/76-web-dancerapp-coverage.t)
(status: pre-fix CSP override red reconstructed; current regression verified)
Expected: hook variables are visible to page/Ajax CODE, hook headers survive defaults, and each skill Dashboard.pm is loaded once at startup.
Root cause: the web adapter applied its default security headers after skill `before` hooks, overwriting an explicit hook response header. The env-variable and once-per-startup behaviors already passed in the reconstructed pre-fix integration run and are not claimed as red in Problem 19.

Audit status for Problems 1–19: current regression tests pass in the full suite,
and release/change records exist. Their historical Docker reproduction logs,
red-before-fix outputs, and per-problem release/security checklists are not
present in the tracked problem report; therefore full historical SDLC-v2
compliance is not claimed from this retrospective audit.

Problem 24: Require patched Pod::Text for CLI help rendering (status: done in 5.34)
Expected outcome: clean dependency installation resolves `podlators`/`Pod::Text` 6.1.1 or later, and the isolated dependency audit no longer fails on CVE-2026-82560.
Reproduction: CI run 36698409996 failed step 7, `Audit isolated dependency root`, after `cpanm --installdeps --notest -L local .`; the audit named `podlators (have v6.0.2)` and exit status 5. CLI help is rendered through `Pod::Usage` in `bin/dashboard`, which uses `Pod::Text`.
Root cause: Pod::Text had no explicit secure minimum in the runtime dependency manifests, so a clean install could select the vulnerable podlators release. The workflow's “environmental - NOT a release blocker” label was inaccurate for a distribution installed in the isolated project dependency root.
Red-first regression: `t/181-podlators-security-floor.t` failed the three manifest assertions in Docker before implementation; its seven packaged-source assertions now pass. `t/108` rejects a synthetic 6.0.2 module before scanning, and all 150 assertions pass with CPAN::Audit installed. A fresh Docker dependency install resolved 6.1.1; the guarded isolated audit exited 0 with no in-scope distribution advisories. The first no-`--notest` tarball attempt exposed a packaging defect: `t/181` required `.github/workflows/test.yml`, intentionally excluded from releases. Moved that CI-only assertion into checkout-only `t/108`. Version 5.33 was already built, so ran `dzil clean`, bumped all 74 modules and metadata to 5.34, and rebuilt. The 5.34 archive passes CPANTS kwalitee 7/7 and POD syntax (292 assertions). Blank-container `cpanm` without `--notest` installed 116 distributions and passed the packaged test harness; `dashboard version` reported 5.34. The full blank-environment integration runner passed, including startup, browser checks, CLI/runtime flows, and shutdown. `d2 docker.images.build` succeeded, and a fresh run of the built `d2` image reported 5.34. A later isolated `d2 docker compose -f .audit-problem2122-compose.yml run --rm --no-deps d2 d2 version` also reported 5.34 without disturbing the long-running service; the 5.34 build helper refused a duplicate same-version build as designed. The already-running `d2-d2-1` service remains an older 5.12 container (created two days earlier) and was not recreated because its ephemeral filesystem may hold user state. A separate 5.33 source-suite run once hit a timing-sensitive `t/153` failure; its isolated rerun passed all 34 assertions and the 5.34 packaged test harness passed. Problem 20 aggregate coverage remains paused and is not claimed.

Problem 24 delivery checklist:
[x] Reproduced CI advisory failure, documented expected outcome/root cause, and wrote red-first dependency-floor regression tests.
[x] Required Pod::Text 6.1.1 in cpanfile, Makefile.PL, and dist.ini; verified the scanned-root module path/version before applying the exact advisory disposition.
[x] Classified the isolated dependency audit as a CI release gate and kept the workflow-only assertion in the checkout-only metadata test so packaged tests do not depend on `.github`.
[x] Updated security docs, main POD/README synchronization, Changes, FIXED_BUGS, and this problem report.
[x] Ran `dzil clean`; aligned dist.ini, all 74 Perl modules, and POD versions at 5.34; `dzil build` produced one archive without `cover_db`.
[x] Focused security suite passed (459 web/security assertions); dependency advisory suite passed (150 assertions); release metadata/regression suite passed (5,072 assertions); CPANTS 7/7 and POD syntax 292 assertions passed.
[x] Blank Docker installation with `cpanm` and no `--notest` passed; the installed version was 5.34. Full blank-environment integration passed.
[x] `d2 docker.images.build` completed; a one-off run of the built `d2` image reported version 5.34.
[x] Commit 1e93111d was pushed to origin/master. Post-push Scorecard ran with the required interactive-shell command and returned 8.0/10; all technical checks scored 10/10, but the external governance checks below remain open.

Problem 21: Discover both runtime roots together (23981132) (status: done in 5.25; verified in 5.30)
Expected outcome: `.d2` and `.developer-dashboard` roots at every home/project depth both participate, with documented same-depth lookup/write precedence.
Reproduction: In the isolated Docker fixture, place commands, config, Docker files, dashboards, skills, and environment files under both root names at home and project depths; run the corresponding dashboard/d2 commands and inspect resolved paths. Before the fix, the `.d2` tree was ignored whenever `.developer-dashboard` existed.
Root cause: path discovery treated the two supported root names as mutually exclusive instead of independent roots at each layer.
Regression: the red-first Docker test failed 11 assertions; after implementation the focused suite passed. The integration plan and path-registry tests cover lookup, writes, and ecosystem consumers. Aggregate coverage is deferred to Problem 20 by user instruction and is not claimed.
Re-run evidence: applying only the Problem 21 test changes from commit `23981132` to pre-fix parent `948225e0` and running `d2 docker compose exec dev prove -l t/25-d2-alias.t` reproduced missing `.d2` layers, ownership, CLI/config/dashboard lookup, and skill precedence (7 explicit failures before the legacy test's unrelated command-not-found exit). Current source passed `t/25-d2-alias.t` in Docker.

Problem 22: Keep `ddfile.local` dependencies private to their owning skill (23981132) (status: done in 5.25; verified in 5.30)
Expected outcome: dependencies declared by an installed skill's `ddfile.local` are installed beneath that skill's own `skills/` directory, never the runtime-global skill directory.
Reproduction: In Docker, create an owner skill with a local manifest naming a dependency, install the owner, then inspect both the owner's nested `skills/` tree and the runtime-wide `skills/` tree. Before the fix, local dependencies could be placed in the shared runtime location.
Root cause: local manifests reused the runtime-wide install destination rather than deriving a contained destination from the owning skill.
Regression: Docker tests cover successful nested installation and reject traversal, symlink escape, invalid names, and recursion hazards. Focused tests and the 5.25 blank-container package install passed. Aggregate coverage is deferred to Problem 20 and is not claimed.
Re-run evidence: applying the Problem 22 assertions from commit `23981132` to pre-fix parent `948225e0` and running `d2 docker compose exec dev prove -l t/102-skillmanager-coverage.t` produced 4 expected failures: dependency not installed under owner, invalid name not rejected, dependency symlink escape not rejected, and symlinked skills root not rejected. Current source passed the same test in Docker. The combined current P21/P22 run passed 467 tests across both files.

Problem 23: Remove PAX from Developer Dashboard (bb14a6fe) (status: done in 5.29; verified in 5.30; coverage remains deferred to Problem 20)
Description: Expected outcome: d2/dashboard commands execute through Perl without a compiler cache, no PAX CLI/helper/modules or PAX-specific release job are shipped, stale compiled artifacts stay out of the archive, and standard commands continue working. Reproduction: in Docker, `dashboard pax build --compact -o /tmp/problem23-dashboard /usr/local/bin/dashboard` compiled 117 application files and 2,596 payloads in about 41 seconds; a fresh-home dashboard invocation also staged the PAX helper. Root cause: the vendored compiler and self-compile cache added a parallel command/runtime/release surface to normal dashboard use. Steps: run the build command; inspect staged helper and module paths; run dashboard and d2 version; inspect the built archive; invoke `t/264-pax-removal-contract.t`. The red-first Docker test initially failed because compiler/cache/helper/workflow artifacts existed; after removal all 12 assertions pass. Archive inspection also exposed stale `pax-output/` files, now excluded by a regression guard. The blank-environment browser gate exposed a Chromium-version difference: HTTP 401 can appear as a generic error page or `ERR_HTTP_RESPONSE_CODE_FAILURE`; both are checked while requiring no bootstrap/login content leakage. Aggregate coverage is expressly deferred to Problem 20 and is not claimed.

Delivery checklist for Problems 21-22:
[x] Docker reproduction, root cause, red tests, implementation, and focused regression verification.
[x] Documentation, Changes, FIXED_BUGS, README/POD alignment, and problem report updated.
[x] Coverage recorded and 100% gate deferred to Problem 20 by user instruction; no 100% coverage claim.
[x] 5.24 package failure reproduced; PAX module import-root defect fixed red-first; t/229 (10 assertions) and t/183 (71 assertions) pass in Docker.
[x] Required security scans and security-test trio pass after the final fix (459 tests).
[x] dzil clean run before bumping all 117 modules and dist.ini to 5.25.
[x] dzil build produced Developer-Dashboard-5.25.tar.gz; embedded module says 5.25, only current tarball remains, no cover_db included.
[x] Fresh blank-container cpanm install with no --notest: 265 files / 16,160 tests passed; CPANTS kwalitee 7/7, 100% in Docker.
[x] d2 docker.images.build completed; scoped D2 Compose runtime reports image version 5.25.
[x] Commit recorded as 23981132 with a problem-numbered subject and structured bullet summary.
[x] Commit 6b9f5faf was pushed to origin/master; post-push Scorecard was run using the required interactive-shell command.
[ ] Scorecard is 8.0/10, not a pass. Branch-Protection is 0/10 because branch protection is disabled; CII-Best-Practices is 0/10 because no badge effort is detected; Code-Review is 0/10 (0/30 approved changesets); Contributors is 0/10 (no contributing organizations). CI-Tests is unknown because there is no pull request to evaluate. Other reported checks are 10/10.
[ ] Follow-up requires repository-admin action to enable branch protection and a genuine PR review process, external participation to earn the OpenSSF Best Practices badge and build organization contributions, then rerun Scorecard. The available interactive token can read repository/user data but its branch-protection API request returns 403; it has no administrator permission. Do not claim these checks passed.

Problem 23 delivery checklist:
[x] Docker reproduction, root-cause analysis, and red-first regression test.
[x] Remove compiler modules/cache, CLI helper, build workflow, and self-compile entrypoint paths.
[x] Focused Docker regression: t/264-pax-removal-contract.t passes (11 tests).
[x] Full non-coverage suite passes in Docker: 203 files / 19,172 tests. One first-run timing failure in t/153 passed both an isolated rerun and the clean full-suite rerun; after adding the archive-exclusion assertion, t/264 passes all 12 assertions.
[x] Required security scans and focused web security tests pass; reviewed perlsec guidance for the removed compiler/launcher boundary. Coverage gate deferred to Problem 20 as requested.
[x] Documentation, version 5.29, Changes, README/POD synchronization, and FIXED_BUGS.
[x] Final dzil clean/build, archive inspection, blank-container cpanm test without --notest (115 distributions), and image build/version verification for 5.29.
[x] Full blank integration runner passed against 5.26 after the Chromium assertion adjustment; final 5.29 package install/tests passed. Post-build t/44 guard is skipped because the dev container has no docker executable.
[x] CPANTS release kwalitee passed 7/7; fresh image containers report dashboard and d2 version 5.29. Aggregate coverage remains deferred to Problem 20.
[x] Commit recorded as bb14a6fe with a problem-numbered subject and structured bullet summary.
[x] Commit 6b9f5faf was pushed to origin/master; post-push Scorecard was run using the required interactive-shell command (8.0/10).
[ ] Scorecard governance follow-up remains open as detailed above. Problem 20's aggregate coverage is separately paused by user request.

Problem 21–23 SDLC-process v2 evidence audit (2026-09-30)
Numbering follows this report: Problem 21 is dual runtime-root discovery,
Problem 22 is private ddfile.local dependency installation, and Problem 23 is
PAX removal. The earlier emoji display report also has a passing regression
assertion in t/14-coverage-closure-extra.t (assertion 165).

| SDLC-process v2 step | Problem 21 | Problem 22 | Problem 23 |
|---|---|---|---|
| 1. Record the problem | DONE — this report, commit 23981132 | DONE — this report, commit 23981132 | DONE — this report, commit bb14a6fe |
| 2. Reproduce in Docker | DONE — pre-fix t/25 red run; 7 assertions failed | DONE — pre-fix t/102 red run; 4 assertions failed | DONE — pre-fix t/264 red run; 9 assertions failed; prior report records 117 files / 2,596 payloads / ~41s for the removed build |
| 3. Find root cause | DONE — roots were treated as mutually exclusive | DONE — local manifest used runtime-global destination | DONE — compiler/cache created a second CLI/runtime/release path |
| 4. Record exact steps and expected result | DONE — reproduction and expected layer precedence in this report; t/25 encodes the fixture | DONE — reproduction and expected owner-local install in this report; t/102 encodes the fixture | DONE — reproduction and expected no-PAX contract in this report; t/264 encodes it |
| 5. Add red test first | DONE — overlay t/25 from 23981132 onto pre-fix 948225e0; 7 expected failures | DONE — overlay t/102 from 23981132 onto pre-fix 948225e0; 4 expected failures | DONE — overlay t/264 from bb14a6fe onto its parent; 9 expected failures |
| 6. Implement and repeat test loop | DONE — PathRegistry changes in 23981132; current t/25 passes | DONE — owner-root containment in 23981132; current t/102 passes | DONE — removal in bb14a6fe; current t/264 passes |
| 7. Rerun regression and relevant suite | DONE — Docker run below: t/25 passes | DONE — Docker run below: t/102 passes | DONE — Docker run below: t/264 passes |
| 8. Rerun original reproduction after fix | DONE — current dual-root fixture passes and confirms precedence | DONE — current owner-local install and traversal/symlink guards pass | DONE — current-source command with isolated HOME prints dashboard usage, exits 1, and creates no output artifact |
| 9. Update README, main POD, related POD, Changes | DONE — recorded in 23981132 and 6b9f5faf; t/15 release-metadata passes | DONE — recorded in 23981132 and 6b9f5faf; t/15 release-metadata passes | DONE — recorded in bb14a6fe and 6b9f5faf; t/15 release-metadata passes |
| 10. Generate/update problem-report | DONE — problem descriptions plus this step-by-step audit are recorded here | DONE — problem descriptions plus this step-by-step audit are recorded here | DONE — problem description, delivery checklist, and this audit are recorded here |
| 11. Run dzil clean | DONE — release sequence recorded in the delivery checklists; not rerun against later source | DONE — release sequence recorded in the delivery checklists; not rerun against later source | DONE — release sequence recorded in the delivery checklist; not rerun against later source |
| 12. Bump dist.ini and module versions | DONE — 5.25 at 23981132; combined delivery 5.30 at 6b9f5faf | DONE — 5.25 at 23981132; combined delivery 5.30 at 6b9f5faf | DONE — 5.29 at bb14a6fe; combined delivery 5.30 at 6b9f5faf |
| 13. Run dzil build | DONE — tarball build/install counts recorded in the delivery checklist | DONE — tarball build/install counts recorded in the delivery checklist | DONE — 5.29 archive/install and final 5.30 build/install recorded in the delivery checklist |
| 14. Run d2 docker.images.build | DONE — 5.25 and final 5.30 image/version results recorded in the delivery checklist | DONE — 5.25 and final 5.30 image/version results recorded in the delivery checklist | DONE — 5.29 and final 5.30 image/version results recorded in the delivery checklist |
| 15. Commit with problem title and detailed summary | DONE — 23981132 and final delivery 6b9f5faf; both are ancestors of origin/master | DONE — 23981132 and final delivery 6b9f5faf; both are ancestors of origin/master | DONE — bb14a6fe and final delivery 6b9f5faf; both are ancestors of origin/master |

Fresh Docker verification (service dev, 2026-09-30):
`d2 docker compose --project-name dev exec dev sh -c 'cd /work && prove -l t/25-d2-alias.t t/102-skillmanager-coverage.t t/264-pax-removal-contract.t t/14-coverage-closure-extra.t t/15-release-metadata.t'`
Result: PASS, 5 files / 5,574 tests. The three red runs used isolated source
archives at pre-fix refs, then overlaid only the regression tests from the fix
commits; failures were P21 assertions 5, 7–12; P22 assertions 275, 278–280;
and P23 assertions 1–7, 9–10. The current-source P23 CLI check used an isolated
HOME and confirmed the removed command is not dispatched.

Verification caveats (do not turn these into passes): the live dev container's
installed d2/dashboard reports 5.32 and its root HOME contains an old
`~/.developer-dashboard/cli/pax`; this stale installed state is not evidence
about the current source or the 5.30 image. The source-only check therefore
used `/work` with an isolated HOME. The last recorded post-push Scorecard is
8.0/10, with repository-governance actions still open as documented below.
Problem 20's earlier aggregate coverage cycle passed; the active Problem 12 and
32 edits introduced new uncovered paths, so the current full-tree coverage gate
is not green yet. Do not treat that historical Problem 20 result as covering
the current uncommitted tree.

Problem 20: Bring repository-wide Perl coverage to 100% (status: complete; verified 2026-10-03)
Description: The isolated Docker coverage gate passed all 257 test files / 22,259 tests and reported 100.0% statement, branch, condition, and subroutine coverage across `lib/`, with no stale uncoverable annotations.

Problem 26: Preserve full branch labels in `ps1` across all shells (master) (status: complete in release 5.37)
Description: `dashboard ps1` must display complete slash-containing branch names rather than only the final path component. When HEAD is detached at a commit referenced by `origin/<branch>`, display `<branch>` without the `origin/` prefix; if no origin ref matches, retain the short commit id. This is shared prompt behavior and applies to Bash, Zsh, sh, and PowerShell adapters.
Expected outcome: `foo/bar` displays as `foo/bar`; detached `origin/foo/bar` displays as `foo/bar`; an unmatched detached commit continues to display its abbreviated SHA.
Reproduction from `/tmp/capture-379.txt`: in the isolated project Docker service, check out `origin/master` detached and run `dashboard ps1`; the old output uses the commit SHA instead of `master`. Then create/check out `foo/bar` and run `dashboard ps1`; the old output truncates the branch to `bar`. Both are reproduced as red-first tests in `t/74-prompt-coverage.t` (two failing assertions before the implementation).
Root cause: the prompt resolver used `basename` for symbolic refs, which chopped nested branch names, and displayed every detached HEAD as a short SHA without checking local origin tracking refs.
Fix: retain the suffix after `refs/heads/`, recognize remote symbolic refs, and resolve detached commits against loose and packed `refs/remotes/origin/*` entries. The resolver is in the common Perl prompt renderer invoked by all shell adapters; no shell-specific implementation was needed. If no ref matches, the existing short-SHA behavior remains.
Verification: red-first Docker run of `t/74-prompt-coverage.t` failed the two original assertions before implementation (nested symbolic ref was reduced to `bar`; detached `origin/foo/bar` displayed `0123456`). A security review then added two more red assertions: symlinked `refs/remotes/origin` and `packed-refs` both escaped the Git metadata root and supplied `foo/bar`. The resolver now refuses both links; all 45 focused prompt tests pass, covering slash branches, loose and packed origin refs, symlink rejection, and unmatched detached HEAD. A real Git repository in the isolated Docker service confirmed `dashboard ps1` prints `🌿master` after detached checkout of `origin/master` and `🌿foo/bar` on local branch `foo/bar`. `t/21-refactor-coverage.t` now runs the generated Bash, Zsh, sh, `ps`, `powershell`, and `pwsh` prompt bootstraps and asserts that each delegates to the shared `dashboard ps1` renderer; all 921 assertions pass. The final full Docker suite passes (206 files / 20,991 tests). The required web security trio passed (3 files / 459 tests), and `podchecker` passed for the changed modules/tests with POD. Optional tooling/operator/browser tests skipped as documented by their test prerequisites. Security review: reads Git metadata, refuses symlinked ref roots/files, parses ref contents as data, adds no shell execution or dependencies. The shared renderer is called by Bash, Zsh, sh, and PowerShell prompt adapters, so the change is shell-independent.
Final coverage-gate audit (2026-10-01, exact tree in Docker dev): this audit belongs to the separate Problem 20 cycle and is recorded there; it is not part of the Problem 7/26 acceptance or release decision per the user's explicit direction. Problem 7/26 release verification proceeds on its own functional, documentation, and security gates.

Problem 26 follow-up: retain `origin/` unless an equivalent local branch exists (master) (status: complete in release 5.38; committed below)
Description: `dashboard ps1` should show a remote branch as `origin/foo/bar` when no local `foo/bar` branch points to the same commit. Omit only the redundant `origin/` prefix when both refs resolve to the identical commit. Apply the rule to symbolic and detached HEADs, and to loose and packed refs.
Expected outcome: remote-only or differing local refs render `origin/foo/bar`; an exact same-commit local `foo/bar` renders `foo/bar`; unrelated detached commits still render their abbreviated SHA.
Reproduction: Docker-run `prove -lv t/74-prompt-coverage.t`. Before the fix, a detached HEAD at loose `origin/foo/bar`, a packed origin ref, and a different-commit local `foo/bar` each incorrectly rendered `foo/bar` (three red assertions). The same-commit local-branch assertion passed, confirming the existing omission was unconditional rather than commit-aware.
Root cause: the shared prompt resolver discarded the remote name unconditionally for symbolic remote HEADs and detached origin matches; it did not compare the local branch object id.
Fix: compare the exact local `refs/heads/<branch>` commit against the remote origin ref, preserving `origin/` if absent or different. Read refs directly and reject symlinked paths; shell adapters continue to share the renderer.
Verification: red-first focused Docker run failed the three new origin/local-branch assertions before the fix. After implementation, `t/74-prompt-coverage.t` and `t/21-refactor-coverage.t` passed (2 files / 971 tests); the complete Docker suite passed (206 files / 21,019 tests); the required web security trio passed (3 files / 459 tests). A live CLI check in Docker used a temporary Git repository and confirmed remote-only `origin/foo/bar` stays prefixed, then confirmed an identical local `foo/bar` suppresses the prefix. README/POD synchronization, changed-file `podchecker`, `git diff --check`, and the required static security scans passed. The first blank-container `cpanm` run exposed process cleanup failures because the test container lacked an init reaper; this was an environment/test-harness failure, not a product pass. Adding `init: true` to the isolated Compose override and rerunning `cpanm` without `--notest` installed all 116 distributions successfully; the blank-environment integration runner then passed. `dzil clean` followed by `dzil build` produced `Developer-Dashboard-5.38.tar.gz`; version metadata is aligned across `dist.ini` and all 75 library modules, and archive inspection confirmed `cover_db` is absent. `d2 docker.images.build` succeeded; a fresh run of its image reported 5.38. No repository-wide coverage percentage is claimed in this Problem 26 cycle; the user designated that as the separate Problem 20 cycle.

Problem 26 follow-up delivery checklist:
[x] Reproduce the incorrect prefix behavior with red-first Docker regression tests and record the expected output, root cause, and reproduction steps.
[x] Compare the pre-fix behavior with the required same-commit-only prefix omission; implement the comparison for symbolic/detached HEAD and loose/packed refs.
[x] Review Git-ref reads for path traversal and symlink escape; reject symlinked ref paths and packed-ref files. No new shell execution or dependency was introduced.
[x] Run focused and full Docker tests, web security tests, the real `dashboard ps1` CLI scenario, static security scans, POD validation, and documentation synchronization.
[x] Update `Changes`, `FIXED_BUGS.md`, README/main POD, and this problem report; run `dzil clean`, align version 5.38 in `dist.ini` and all 75 library modules, and build the archive.
[x] Install the tarball in a blank Docker environment with `cpanm` and tests enabled; run the blank-environment integration flow; build the Docker image and verify its reported version is 5.38.
[x] Commit the verified Problem 26 follow-up. Push and post-push Scorecard are not performed in this cycle.

Problem 7/26 SDLC completion (2026-10-01): the complete Docker suite passed 206 files / 20,991 tests; focused Problem 7/26 regressions passed 3 files / 1,215 tests; the required web-security trio passed 3 files / 459 tests. README/POD synchronization, changed-file POD checks, static security scans, and `git diff --check` passed. The blank-environment integration flow passed; blank-container `cpanm` with tests enabled installed all 116 distributions successfully. `dzil clean` removed the previous 5.35 artifact; `dzil build` produced 5.37. Archive inspection confirmed version metadata and that `cover_db` is excluded. `d2 docker.images.build` succeeded, and an isolated Compose run of that image printed `5.37`. The changes are committed on `master`; Problem 20 remains a separate coverage cycle and is not claimed complete here.

Problem 20 resumed audit (2026-09-30, source commit f56aedc2):
The first full-gate attempt used root with DAC-bypass capabilities dropped while the checkout was mounted as ubuntu-owned; t/110 and t/111 could not create fixtures, so that run was invalid and was stopped. A clean root-owned clone of the same commit was then tested in Docker with the same capabilities dropped. `prove -lr t` passed 204 files / 19,137 tests. The four-metric gate correctly failed: statement 99.9%, branch 99.2%, condition 98.6%, subroutine 99.9%, aggregate 99.5%. Missing coverage remains concentrated in CLI::Paths, SkillDispatcher, Web::App, PageRuntime, EnvLoader, DockerCompose, SkillManager, and filesystem failure paths. Specific report rows were inspected from `/tmp/problem20-coverage-final-20260930`; no coverage percentage is claimed as complete.

Resumed coverage iteration (2026-09-30): A non-root Ubuntu test run in the `dev` Docker service passed 204 files / 19,319 tests. `t/09` initially exposed a fixture hard-coded under `/tmp/user`; it now directs state into its temporary HOME. Iteration 6 completed but remained short: statement 99.9%, branch 99.6%, condition 99.2%, subroutine 99.9%, aggregate 99.7%. Additional focused regressions were added for skill folder alias edge cases, env.pl dynamic assignments, Docker development-marker cwd defaults, saved-URL query merging, and SkillManager branch-selection diagnostics; their focused Docker tests pass. Iteration 7 passed 204 files / 14,236 tests and improved condition coverage to 99.3% (statement 99.9%, branch 99.6%, subroutine 99.9%, aggregate 99.7%), but was invalid for final coverage because its archive-only clone omitted `.git`, so source-tree checks including `t/139` skipped. A separate Docker test using a local git clone passed all 14 assertions in `t/139`.

Iteration 8 used that git-backed isolated clone, passed 204 files / 19,212 tests, and ran `t/139` plus the source-tree guardrails. The strict gate still fails: statement 99.9%, branch 99.6%, condition 99.3%, subroutine 99.9%, aggregate 99.7%. Remaining non-100 modules are CLI::Paths, DockerCompose, EnvLoader, IndicatorStore, PageRuntime, SkillDispatcher, SkillManager, Web::App, and Zipper. Full gate output and per-module detail are retained in the Docker service at `/tmp/problem20-coverage-iter8-20260930`; next work must target those exact missing branch/condition rows. No full-gate success yet. Versioning, documentation/release gates, and packaging have not been started for this resumed cycle.

Coverage-gap reproduction exposed an additional bookmark crash: save `//example.test/path` as a bookmark and call `_legacy_app_response`; before the fix it died because `URI::_generic` has no `host` method. The expected result is an HTTP redirect preserving the protocol-relative `Location`. The regression was added first in `t/105-web-app-coverage-2.t` and failed with that exception. The implementation now checks the generic URI's `authority` and only asks for `host` when the URI class supports it; the focused Docker test then passed. This fix and the newer `t/104`/`t/105` cases still need inclusion in a fresh full coverage gate.

Scorecard gate (2026-09-30, after pushed commit 93218224): status incomplete, 8.0/10. Live output is from `bash -ic "scorecard --repo=github.com/manif3station/developer-dashboard"`. All checks other than Branch-Protection, CII-Best-Practices, Code-Review, Contributors, and CI-Tests scored 10/10; CI-Tests is unknown because no pull request was found. Branch-Protection is 0/10; CII-Best-Practices is 0/10; Code-Review is 0/10 (0/30 approved changesets); Contributors is 0/10 (0 contributing organizations). Read-only GitHub API verification found 6 merged PRs and no approval reviews; the branch-protection endpoint returned HTTP 403 `Resource not accessible by personal access token`. The interactive shell's `GITHUB_AUTH_TOKEN` authenticates API reads as `manif3station`, although `gh auth status` itself says no host login because that environment token is not configured as a `gh` login. Remaining work requires an administrator-capable token and repo-owner decisions on branch protection, OpenSSF Best Practices enrollment, and inviting independent review/contributor organizations; the 6 merged PRs cannot be retroactively turned into honest approvals. These are not fixable by source edits alone; do not claim the overall goal is complete until actionable Scorecard requirements are satisfied and rescanned.

Problem 25: Complete help and shell-completion coverage for internal CLI commands (status: original help catalog delivered; delegated CLI help regression fixed in 5.42)
Expected outcome: every shipped internal CLI command and each actionable subcommand supports `--help`, `-h`, and/or an explicit `help` form with actionable syntax, exits successfully without performing the command, and appears in relevant TAB completion. Completion must stay synchronized with the command dispatch surface.
Scope discovered: all 39 assets registered by `Developer::Dashboard::InternalCLI::helper_names()` under `share/private-cli/`, plus the direct switchboard `version` command. This includes direct helpers (`jq`, `yq`, `tomq`, `propq`, `iniq`, `csvq`, `xmlq`, `of`, `open-file`, `workspace`, `file`, `files`, `path`, `paths`, `ps1`, `source`, `which`, `upgrade`) and shared-dispatch helpers (`encode`, `decode`, `indicator`, `collector`, `config`, `auth`, `api`, `ask`, `init`, `cpan`, `page`, `action`, `docker`, `serve`, `stop`, `restart`, `log`, `shell`, `doctor`, `housekeeper`, `skills`, `complete`). Aliases also need parity. Nested action inventory follows.
Nested action inventory verified against dispatch: `api` = `ls add rm`; `auth` = `add-user list-users remove-user`; `collector` = `write-result status list job output inspect log run start stop restart`; `config` = `init show`; `docker` = `compose list enable disable development` and `docker development` = `enable disable`; `file` = `resolve locate add del list`; `indicator` = `set list refresh-core`; `page` = `new save list show encode decode urls render source`; `action` = `run`; `skills` = `install uninstall enable disable list usage`; `path` = `resolve locate cdr complete-cdr add del rm project-root list`; `restart`, `stop`, `log`, and `logs` = `web collector`; `serve` = `logs workers`; `shell` accepts `bash zsh sh ps powershell pwsh`. `files`, query commands, and the remaining direct helpers use flags/positional operands rather than nested actions. Internal-only `skills _exec` and foreground worker commands are dispatch implementation details, not public actions.
Reproduction in isolated Docker: `d2 docker compose --project-name problem25 run --rm --no-deps dev ...`; use a temporary HOME and invoke `/work/bin/dashboard`. Observed `api --help` reports `Unknown option: help` and continues into API listing; `file --help` and `path --help` only show usage/error text rather than help. `dashboard complete 2 dashboard api` and `... file` return no candidates; path completion omits the implemented `cdr`, `complete-cdr`, and `rm` actions. Existing long-running `d2-d2-1` was not touched.
Root cause: help behavior was scattered across the top-level POD, `ask`, and `docker`, while many internal helpers only emitted usage on invalid input. Second-level completion was maintained as a separate partial hard-coded list, already out of sync with `api`, `file`, and `path` dispatch. A final-audit pass found that `api` accepts its default `ls` options without an explicit action but did not expose those flags at root-level help/TAB. The next pass found that `version` is a public switchboard command outside the staged-helper registry, so the original catalog omitted both `version --help` and top-level TAB completion for `version`. A later manual audit found bare `help`/`--help` printed the entire 244-line module POD; the global entrypoint now renders a concise catalog-backed index instead.

Red-first evidence: the initial Docker subprocess test failed because `api --help` emitted a Getopt warning and continued into the listing, file/path help exited with usage failures, nested action help executed or errored instead of rendering its synopsis, and the generic `help <command> <action>` route ignored the requested action. After adding option completion assertions, the contract test independently failed on missing `--jobs`, `--secret`, `--create`, `--addon`, `--branch`, and lifecycle flags before the catalog-backed completion was implemented. A follow-up audit test caught incorrect alias normalization for `logs` and a test expectation that did not match Pod::Usage's `Name:` heading; both were corrected.

Final-audit red/green evidence: the default-`api ls` regression first failed in Docker because root TAB omitted `--key`, `--output`, and `-o`, and root help omitted the long output spelling. The shared catalog change made these pass through both direct completion and the generated Bash function. The next audit expanded the catalog contract to include public `version`; before implementation the registry equality check failed, and the subprocess integration did not yet route `version --help` through help. The switchboard now dispatches help for `version` before preserving ordinary `dashboard version` output, and the command suggestion source includes `version` for TAB. Most recently, six new assertions failed before the overview implementation: all three global spellings emitted the module POD, omitted the catalog index, and exceeded the 100-line bound. The first implementation exposed an empty list caused by Perl parsing `sort command_names()` as a comparator; replacing it with an explicit array restored the catalog. The updated CLI smoke assertions and all overview checks now pass.

Final package-gate audit: the first clean-image `cpanm` run without `--notest` exposed that completing `dashboard workspace -` queried tmux before suggesting option flags; in an image without tmux this died after the contract assertions. A red-first provider-injection regression reproduced it, and completion now queries workspace sessions only for positional words. The first package run also found stale release metadata: `t/15` hard-coded 5.34 and unconditionally required an absent `doc/install-bootstrap.md`. It now compares `lib/Developer/Dashboard.pm` to `dist.ini` and requires that optional document only when Git tracks it, so ignored operator-local Markdown cannot influence release assertions. Running the package suite in a container without an init reaper also caused process-cleanup timing failures; the isolated package service now uses an init process. After these corrections, the blank-image `cpanm` run without `--notest` installed 116 distributions successfully.

Final verification: after the final release-metadata guard, the full Docker suite passed 206 files / 20,788 tests and all six P25-focused files passed (6 files / 7,542 tests in the clean release clone). The required web security trio passed (3 files / 459 tests). The blank-environment integration runner passed, including the installed help index, browser/runtime lifecycle, and shutdown. The 5.35 tarball has matching `dist.ini`, main module/POD, and 75 library-module versions; archive inspection found neither `cover_db` nor `.worktrees`. `dzil clean` followed by `dzil build` succeeded in the clean release clone. `d2 docker.images.build` succeeded after supplying the release version and placing the clone-built tarball at the helper's configured source root; an isolated one-off run of the new image reported 5.35. The helper's first attempt stopped before image creation because its configured root lacked the 5.35 tarball, so the retry used the script's explicit same-version retry override after placing the already-tested artifact.

Problem 25 task list:
[x] Enumerate internal helper assets, shared-dispatch actions, and current completion gaps; reproduce representative failures in an isolated Docker Compose run.
[x] T1: Add red-first contract tests and a shared help catalog for all 39 registered helpers, aliases, and the public `version` command; verify root help returns successfully before command bodies run.
[x] T2: Add per-action help for the nested action inventory, including the nested Docker development actions; verify each action help spelling and synopsis in subprocess integration tests.
[x] T3: Align action TAB candidates with help metadata and add option candidates for parser-backed command/action contexts. The option inventory was audited against each `GetOptionsFromArray` declaration; unsupported `cpan --dry-run` was removed from help and negated boolean spellings were corrected.
[x] T4 (focused and full): Run all P25 regressions, the full Docker suite, and required web security tests. Final runs passed 6 files / 7,542 focused tests, 206 files / 20,788 total tests, and 3 files / 459 web/security tests. Bash behavior and zsh hook registration are tested; interactive zsh is unavailable in the development image.
[x] T4 (final audit): Recheck every registered helper, alias, nested action, parser-declared option, direct `version` command, global-help target, default `api ls` flags, terminal nested-help completion, and concise overview. The workspace option/TAB/tmux failure was added as a failing regression and then passed. `perlsec` was inspected through `pod2text` because `perldoc` has no formatter in this image. Required security scans found no active forbidden-library imports or SQL execution path. `git diff --check` passed. Run `t/139` only from a regular isolated clone, not a linked host worktree, because it prunes git worktrees from inside Docker.
[x] T5a: Synchronize README with main POD; document the CLI contract; update Changes and FIXED_BUGS.
[x] T5b: Run `dzil clean`, bump `dist.ini` and all 75 library modules to 5.35, and build the release archive. `t/15` verifies version synchronization and package contents.
[x] T5c: Install the tarball in a blank Docker image using `cpanm` without `--notest` (116 distributions); run the complete blank-environment integration flow successfully.
[x] T5d: Build the 5.35 Docker image with `d2 docker.images.build`; an isolated one-off run of that image returned `5.35`.
[x] T5e: Commit only Problem 25 files/version hunks; Problem 20 source/test changes remain uncommitted and separate.
Problem 20 remains separate; its existing worktree changes and incomplete aggregate coverage must not be included or claimed complete here.

Problem 12/25 follow-up regression cycle (2026-10-02)

Problem 12 follow-up: Resolve `dashboard workspace <skill>.<alias> -c` through
the skill Folder provider (status: complete; release 5.44)
Expected outcome: configured aliases and skill `lib/Folder.pm` aliases change
the workspace process into the returned directory before tmux session planning.
The behavior works for top-level, nested, and deeper dotted skill names;
configured aliases win collisions with Folder.pm methods, and real resolver
errors remain visible.
Reproduction: in the isolated Compose `dev` container, create skills
`bar`, `bar.baz`, and `bar.baz.qux`, each with `lib/Folder.pm`, a `__list__`
method, and an alias method returning an existing directory. Run
`dashboard workspace bar.root -c`, `dashboard workspace bar.baz.nested -c`,
and `dashboard workspace bar.baz.qux.leaf -c` with tmux stubbed. Before the fix,
`registered_workspace_dir` returned an empty path for Folder.pm aliases and the
CLI rejected the `-c` request as unregistered.
Root cause: the workspace resolver loaded JSON configuration into
PathRegistry but did not invoke the existing secured skill Folder.pm resolver.
Red-first evidence: the new `t/91-cli-ticket-coverage.t` assertions failed for
top-level and nested Folder aliases (`got: ''`), and the public CLI test
reported that the alias was not registered. After implementation, these cases,
the config-first collision guard, and a public CLI invocation verified that the
tmux `new-session -c` target is the deepest alias directory.

Problem 25 follow-up: Preserve native help for delegated CLIs (status: complete;
release 5.44)
Expected outcome: `dashboard of grep --help`, `dashboard docker compose
config --help`, and `dashboard docker compose help` reach grep or Docker
Compose. The Dashboard wrapper still documents itself via
`dashboard docker compose --help` or `dashboard help docker compose`.
Reproduction: run those commands in the isolated Compose `dev` container. Before
the fix, grep help was intercepted and failed as `Unknown action 'grep' for
dashboard command 'of'`; Docker Compose subcommand help rendered Dashboard
wrapper help instead of reaching Docker Compose.
Root cause: Help.pm interpreted every trailing help marker as Dashboard help;
the grep adapter then treated grep's help output as file-match output. Red-first
tests in `t/265-cli-help-completion-contract.t` and
`t/266-cli-help-dispatch.t` failed on the captured help target and output. The
fix marks explicit passthrough namespaces and execs grep unchanged for
`--help`; Docker Compose's resolved `-f` arguments are preserved.

Problem 25 follow-up: Restrict Dashboard help interception to internal CLI
commands (status: in progress; release 5.51; Docker image gate failed)
Expected outcome: `d2 <internal-command> --help` and its supported `help`/`-h`
forms show Dashboard's internal help. External tools own their flags, including
`d2 tira.tasklist.prune --help`, which must display the skill executable's
native help and return its exit status.
Docker reproduction: in the isolated `dd-problem25` Compose `dev` container,
create a fake installed skill at `$HOME/.d2/skills/tira/cli/tasklist.prune`
that prints `Tira tasklist prune native help`, then run
`perl -Ilib bin/d2 tira.tasklist.prune --help`. Before the fix, it exited 255,
printed no stdout, and failed with `Unknown action '_exec' for dashboard
command 'skills'`.
Root cause: the switchboard correctly translated the dotted command to
`skills _exec <skill> <command> ...`, but the staged private helper ran the
internal help detector again. `help_request` interpreted `_exec` as a public
`skills` action when it found the external command's trailing help flag.
Red test: the new `t/266-cli-help-dispatch.t` fixture failed its three exact
assertions (exit status, native help output, and empty Dashboard-error stderr)
before the implementation change.
Fix: `help_request` now returns no Dashboard help request when the canonical
command is `skills` and the first argument is the private `_exec` sentinel.
The dispatcher then passes every remaining argument, including `--help`, to
the external skill CLI. Built-in `skills --help`/action help and existing grep
and Docker Compose passthrough cases remain covered.
After the fix, the same Docker reproduction passed; `t/266-cli-help-dispatch.t`
passes 463 assertions. The complete Docker suite passed (257 files, 22,957
tests; environment-dependent Windows QEMU, Chromium, nested-Docker, and
operator-local `.claude` checks skipped with their documented reasons). A
separate full `Devel::Cover` run passed 257 files / 22,895 tests and reported
100.0% statement, branch, condition, and subroutine coverage across `lib/`.
The 5.51 tarball installed in the clean blank-environment container using
`cpanm` with its default test execution; the integration script completed
successfully. The Kwalitee and POD tests passed in the Docker suite.

Packaging used `dzil clean`, aligned `dist.ini` and all 76 `lib/` module
versions at 5.51, synchronized README with the main module POD, then built
`Developer-Dashboard-5.51.tar.gz`. However, `d2 docker.images.build` did not
produce the required 5.51 runtime image: the Dockerfile build failed on its
first `RUN curl ... | sh` layer with `mount options is too long`. The wrapper
continued to a host-side tarball install and returned exit 0 despite that
image failure. This gate is recorded as failed, not passed; Problem 25 remains
in progress and must not be committed as complete until image-build
verification is resolved. No commit has been made for this follow-up.

Problem 36: Merge project and home skill command names in shell completion
(status: in progress; release 5.52)
Expected outcome: while inside a project, `d2 layered-skill.<TAB>` lists
commands from both `.developer-dashboard/skills/layered-skill/cli` in the
project and the same skill under the home runtime. A command name present in
both is listed once; runtime execution precedence remains deepest project
layer first.
Docker reproduction: `t/804-skill-completion-layer-merge.t` creates a
temporary home and a Git project. Both layers contain `layered-skill`; home
provides `home-only` and `shared`, project provides `project-only` and
`shared`. Run `prove -lv t/804-skill-completion-layer-merge.t` through the
dedicated `dd-problem36` Docker Compose service. Before the fix, the single
completion assertion failed: only `layered-skill.project-only` and
`layered-skill.shared` were returned, and `layered-skill.home-only` was
missing.
Root cause: `PathRegistry::installed_skill_roots` intentionally emits only
one effective root per skill name. `Suggest::_all_skill_command_entries`
iterated those names, then `_skill_command_entries` called
`SkillManager::get_skill_path`, which again selected only the winning root.
That behavior was appropriate for execution lookup but incomplete for a
completion inventory that must merge inherited command names.
Red test and fix: the new regression checks direct completion and the public
`dashboard complete` helper, including duplicate suppression. The fix uses
`PathRegistry::skill_roots_for` to scan every effective root from project to
home and returns each dotted command name once. Both the direct API and public
helper now return `layered-skill.home-only`, `layered-skill.project-only`,
and one `layered-skill.shared` candidate. Focused completion/help tests pass;
full-suite, coverage, package, and image gate evidence will follow.

Focused Docker verification after implementation: `t/10-extension-action-docker.t`,
`t/91-cli-ticket-coverage.t`, `t/98-cli-openfile-coverage.t`,
`t/103-collectorrunner-coverage.t`, `t/770-cli-exec-handoff-coverage.t`,
`t/265-cli-help-completion-contract.t`, and `t/266-cli-help-dispatch.t` passed
(7 files, 2,379 tests). A first full suite on the mounted checkout showed
environment-only Git/adjacent-operator-state failures; a clean container clone
passed those checks and all suite files except README/POD sync, which was
corrected with `script/sync-readme-from-pod`. A fresh complete-suite and
four-metric coverage result, release build, image build, security audit,
blank-container install, and commit reference will be recorded here after the
final release gates.

Problem 27: Restore `of` content grep and complete Perl `@INC` lookup
(git ref: this delivery commit) (status: done; 2026-10-02)
Expected happy path: `d2 of grep -nr needle <dir>` searches file contents recursively and opens each matching file once; `d2 of --print grep -nr needle <dir>` prints the unique paths without starting an editor. `d2 of Foo::Bar` finds source files under every existing directory in `@INC`.
Reproduction: create a nested source file containing `needle`, a second matching file, and a non-matching file; run both grep forms above. Put the same `Foo/Bar.pm` fixture under two independent `@INC` roots and resolve `Foo::Bar`. Before the fix, grep arguments were treated as path-name patterns and the content matches were not opened; the old module test checked only one extra `@INC` root.
Root cause: path-regex search and file-content grep shared a resolver path, so grep's flags were parsed as path patterns. The Perl module search already traversed `@INC`, but regression coverage did not assert all roots. Red-first tests now check recursive matching, deduplication, colon-containing filenames, no-match/error cases, print mode, editor dispatch, CLI behavior, and multiple `@INC` roots. The fix uses an argv-only grep adapter and the existing chooser; grep is not invoked through a shell.
Verification: the Docker regression harness passed the CLI/open-file/collector tests; the full suite, four-metric coverage, release metadata, security tests, package build, image build, and blank-container install results are recorded below.

Problem 28: Resolve collector working-directory path aliases
(git ref: this delivery commit) (status: done; 2026-10-02)
Expected happy path: a collector's `cwd` accepts a configured `path_aliases` key or `<skill>.<Folder.pm method>`; a configured alias wins on name collision. The built-in `home` accessor remains unchanged.
Reproduction: configure a collector with `cwd` set to a configured alias whose target is an existing directory, then run it. Repeat with an installed fixture skill containing `lib/Folder.pm` whose method returns an existing directory and use `<skill>.<method>` as `cwd`. Before the fix, only hard-coded PathRegistry accessors were recognized, so other aliases were reported as a nonexistent relative cwd.
Root cause: `CollectorRunner::run_once` dispatched only the fixed accessor allowlist and never loaded configured aliases or the secured skill Folder alias provider. Red-first tests now exercise both alias sources, invalid aliases, and the registry contract. The resolver registers config aliases first, then consults the existing guarded Folder.pm resolver; aliases from skills remain read-only.
Verification: the Docker regression harness exercised both alias sources and unknown-alias failure; full-suite, coverage, security, and packaging results are recorded below.

Problem 29: Collector stop propagation (status: needs investigation)
The user asked whether `d2 stop`, `d2 stop collector`, and stopping a named
collector terminate collector-spawned child processes. The historical report
does not contain the requested Docker reproduction result or a final status.
Do not infer that dispatcher shutdown also stops child processes without
checking the current implementation and reproducing it in the isolated Docker
container.

Problem 30: Repair config.json collector cron scheduling (status: complete; release 5.44)
Expected happy path: a collector with a valid five-field `cron` expression in
the effective config.json runs once in each matching local-time minute, follows
normal crontab date matching, and does not run on non-matching minutes.
Missing or malformed cron configuration must produce a visible start error,
not an every-second job.
Reproduction: in a Docker development container, call `_cron_due` with a
five-field expression whose day-of-week matches the current date while its
day-of-month does not; also exercise `JAN`, `MON-FRI`, and `1-15/2`, and attempt
to start a collector with `schedule: cron` but no expression. Before the fix,
the date case was rejected, the named/range-step fields were unmatched, and a
missing expression was due every scheduler tick. The new regression also loads
a wildcard cron entry from config.json and verifies same-minute deduplication.
Root cause: the parser only matched numeric exact values, bare numeric ranges,
and wildcard steps; `_cron_due` incorrectly required both restricted date
fields to match; and its empty-expression fast path returned due immediately.
Red-first evidence: new assertions in `t/103-collectorrunner-coverage.t`
failed on all five identified behaviors in the isolated Docker service.
Implementation: validate exactly five fields and expand supported values into
bounded sets; accept case-insensitive month/weekday names, lists, ranges,
range steps, and `*/step`; apply crontab's OR rule when both date fields are
restricted; return not-due for invalid/missing expressions and reject them at
loop startup before fork. Document the format in the collector reference and
main user documentation. Focused Docker verification passes
`t/103-collectorrunner-coverage.t` (335 tests). The clean full coverage gate
passes 257 files / 22,259 tests with 100.0% statement, branch, condition, and
subroutine coverage.

Problem 27/28 shared delivery verification:
Docker reproduction/verification: `d2 docker compose --project-name dd-problem2728-regression -f /tmp/dd-p27-28-compose.yml up --build --abort-on-container-exit --exit-code-from regression regression` passed the focused CLI/open-file/collector regression tests (3 files, 538 tests); `d2 docker compose ... down` stopped only that unique project. This confirmed the implemented command/alias behavior inside a container.

Full host regression: `prove -lr t` passed (257 files, 22,806 tests). Its tarball-dependent guard was skipped because the tarball had not yet been built. After packaging, `t/44-smart-router-two-stage.t` was retried and skipped with the exact reason `docker daemon is not reachable for the post-build smart-router two-stage guard`; the blank-container cpanm run did run the distribution tests, but the separate Docker smart-router guard remains unavailable on this host. The complete four-metric coverage gate passed (257 files, 22,744 tests); every `lib/` file reported 100.0% statement, branch, condition, and subroutine coverage, with no stale uncoverable annotations.

Post-version targeted checks: `prove -lv t/15-release-metadata.t t/98-cli-openfile-coverage.t t/103-collectorrunner-coverage.t t/69-cli-complete-coverage.t` passed (4 files, 5,928 tests). Required web/security checks passed (3 files, 459 tests). Required security scans found only expected policy text, guard-test patterns, and fixture values; no new prohibited runtime usage, credentials, or sensitive data.

Release verification: the first 5.40 archive audit found root `cover_db_*` databases in the tarball; the previous gather rule excluded only the exact `cover_db` name. Added a red-first release-metadata assertion and extended both gather exclusion and directory pruning to cover the default database and all root-level `cover_db_*` variants. `dzil clean` removed the old archive; `dist.ini`, README/main POD, and all Perl modules under `lib/` now report 5.41. `prove -lv t/15-release-metadata.t` passed (5,391 assertions), `dzil build` produced the sole archive `Developer-Dashboard-5.41.tar.gz`, and inspecting all 570 archive entries found no `cover_db` paths. `d2 docker.images.build` succeeded with 5.41. The 5.41 tarball installed in a blank Perl 5.44 Docker container using plain `cpanm` (no `--notest`); all distribution tests passed and 121 distributions were installed. The Compose project used `init: true` and was removed after the successful run.

Delivery commit: the Problem 27/28 code was committed as `78d6573d`. The 5.41 archive-hygiene follow-up is committed separately. No push was requested.

Problem 31: Honor Docker service markers at every layer (status: complete; release 5.44; selected-home marker write rule reconfirmed 5.45)
Expected happy path: any active `config/docker/<service>/disabled.yml` marker
disables that service; any `develop.yml` marker enables its development overlay.
`docker enable <service>` removes every matching `disabled.yml`, and
`docker development disable <service>` removes every matching `develop.yml`.
New marker files are always created under the selected home runtime.
Reproduction: put a marker in the home service folder and create a deeper
project service folder without a marker. Before the fix, the resolver consulted
only the deepest matching folder, so the shallower marker had no effect. Add
both marker files at multiple depths, then run the corresponding enable/disable
command; before the fix, only the deepest marker was removed. Marker creation
also previously followed the deepest runtime layer instead of the selected
home runtime.
Root cause: `_service_folder_is_disabled` inspected only the last lookup root,
`_service_folder_is_development` returned the deepest marker state immediately,
and toggle commands unlinked one calculated path. `_service_toggle_root` used
the deepest runtime layer. Red-first evidence: new tests in
`t/10-extension-action-docker.t` failed in the isolated container for both
shallower-marker resolution cases and all-layer cleanup. The implementation
now scans every discovered service root, removes markers from every matching
layer with visible errors, and creates markers under `PathRegistry`'s selected
home runtime name. The focused Docker regression `t/10-extension-action-docker.t`
passes; `t/361-dockercompose-coverage.t` covers both marker-presence branches,
including dangling symlink cleanup. The clean full coverage gate passes 257
files / 22,259 tests at 100.0% for all four metrics. Required web/security
tests pass (3 files / 459 tests); package and image evidence is recorded after
the release build below.

Problem 12/25/30/31 final delivery evidence (2026-10-03)
The full Docker coverage gate passed 257 files / 22,259 tests with 100.0%
statement, branch, condition, and subroutine coverage across `lib/`. Focused
Docker tests passed 8 files / 7,894 tests. Required web security tests passed
3 files / 459 tests. `git diff --check` passed. Security scans found expected
policy assertions, test fixtures, and documentation references only; no
prohibited production dependency use or newly exposed secrets. `perlsec` was
reviewed from the installed POD source because the container lacks a POD
formatter (`perldoc -T perlsec` cannot run there).

Release sequence: `dzil clean` removed the previous local build; `dist.ini`,
the main module POD, and all 76 Perl modules use version 5.44; `t/15` verified
version synchronization. `dzil build` produced
`Developer-Dashboard-5.44.tar.gz`, the only root tarball. The archive reports
POD version 5.44 and contains no `cover_db` paths. `d2 docker.images.build`
succeeded, and an isolated run of the resulting image printed `5.44`. The
blank Perl 5.44 container installed the 5.44 tarball with plain `cpanm` (no
`--notest`); dependency and distribution tests passed and the integration
harness reported `Blank-environment integration run passed`. Its unique
Compose project exited 0 and was removed. Post-push Scorecard verification is
not recorded because no push was requested; the repository-side verification
above does not claim that external status.

Problem 12 follow-up: Resolve dotted workspace names as tmux session names
(git ref: pending) (status: functional regression passes; release pending)
Expected outcome: `d2 workspace ch.docker -c` changes to the configured alias
directory and attaches to the tmux session created for logical workspace
`ch.docker`. A concurrently-created duplicate may be reused only after the
session's existence and logical `WORKSPACE_REF` are verified; unrelated tmux
errors and normalized-name collisions remain visible.
Reproduction: in Docker, `tmux new-session -d -s ch.docker` creates a session
listed as `ch_docker`; `tmux has-session -t ch.docker` instead parses the dot as
a session/window target and exits 1 (`can't find pane: docker`). The previous
workspace code queried `ch.docker`, then attempted `new-session -s ch.docker`,
which tmux normalized to the already-existing `ch_docker` and rejected as a
duplicate. Red-first assertions in `t/91-cli-ticket-coverage.t` exposed the
query/create mismatch. The fix consistently uses tmux's underscore-normalized
name, preserves the dotted logical reference in the environment, rejects a
tagged collision with another workspace, and rechecks confirmed duplicate
creation races. Docker reproduction now lists `ch_docker` and reports
`workspace=ch.docker tmux_session=ch_docker reused=1 created=0`.

Problem 32: Separate command/path completion and make cdr completion responsive
(git ref: pending) (status: functional regressions pass; release pending)
Expected outcome: `d2 <TAB>` and `d2 <skill>. <TAB>` offer command candidates,
not path aliases; `d2 workspace <TAB>` offers configured and Folder.pm path
aliases beside existing tmux sessions. `cdr` TAB completion lists direct
children and descends one directory level for each entered narrowing term; it
must not recursively walk unrelated descendants.
Reproduction: install a skill exposing both `cli/run` and a `Folder.pm` alias,
then compare `d2 <skill>.` against `d2 workspace <skill>.`; run cdr completion
from a large checkout containing nested dependency trees. Before the fix, the
top-level dotted skill completion mixed commands and path aliases, workspace
completion omitted aliases, and `cdr` completion recursively walked directory
trees (the Docker repository fixture returned 1,350 candidate lines). Red-first
tests in `t/69-cli-complete-coverage.t` and `t/90-cli-paths-coverage.t` failed
for these mismatches. The fix separates candidate namespaces and narrows `cdr`
one level per entered term. Docker verification returned 40 immediate
candidates in 0.131 seconds. Regression tests passed in Docker. The current full
suite passed 257 files / 22,371 tests. A test-only assertion added to
`t/69-cli-complete-coverage.t` exercises the sole previously uncovered
condition; after rerunning it under Devel::Cover, the aggregate checker reports
all four library metrics at 100.0%. The coverage-gate command itself must be
rerun on the final test tree before release can claim a clean gate.

Problem 33: Keep an unregistered first `cdr` word as a search pattern
(status: behavior verified; regression guard added)
Expected outcome: when the first `cdr` argument is not a registered path alias,
it remains the first regex search term, followed by any later terms; it is not
dropped or treated as a fatal alias lookup. TAB completion must apply that
first word while narrowing candidates.
Reproduction: from a fixture directory containing `alpha/red/target`, run
`cdr alpha red` and `dashboard path complete-cdr 2 alpha red`. The implementation
already retained all words when alias lookup failed; new assertions in
`t/90-cli-paths-coverage.t` prove target resolution and later-term narrowing.
The Docker regression passed. No production change was necessary for this item.

Problem 34: Limit ecosystem Compose services to the local Compose project
(git ref: master) (status: implementation, release gates, and runtime image verification pass; ignored local image recipe still has a repeatability caveat)
Expected outcome: if the invocation directory contains a supported local
`compose.yml`, `compose.yaml`, `docker-compose.yml`, or `docker-compose.yaml`,
that file is the base project. Runtime-discovered services absent from the
local Compose `services:` map are ignored by default; services present locally
can receive their matching ecosystem overlays. Explicitly requested services
and marker behavior must be tested separately so this default does not silently
block deliberate opt-in.
Reproduction: create a local compose file declaring only `foo`, add runtime
service folders for `foo`, `bar`, and `bob`, then run `d2 docker compose config`.
Expected output includes local `foo` and its applicable overlays, not automatic
`bar` or `bob` definitions. Root cause: unscoped auto-discovery gathered every
enabled ecosystem service without intersecting with the invocation directory's
Compose service map. The Docker red test reproduced that mismatch. The resolver
now parses local base service names, filters automatic discovery, preserves
explicit selectors and no-local-file behavior, and reports malformed YAML.
The initial Docker regressions and full suite passed for the original behavior.
The later operational-command follow-up, full release gates, and image-version
verification are recorded immediately below.

Problem 34 operational-command follow-up (2026-10-05): before the fix, the
materialized command for `up`, `build`, and `down` used only
`-f /tmp/.../merged-compose.yml`; Docker Compose therefore derived the project
directory and relative-path root from the temporary file instead of the
invocation directory. A red-first Docker regression failed for all three
actions. The final command now supplies the resolved invocation directory via
`--project-directory`, unless the user supplied that option explicitly.
Red-first tests also exposed missing validation for malformed project-directory
and undefined arguments; these now fail before Compose runs. The focused Docker
run passed `t/94-dockercompose-coverage.t` (188 assertions). An isolated real
Compose project also completed `d2 docker compose build`, `up -d`, and `down`
with a relative Docker build context, confirming all three operations preserve
the invocation project directory. The full suite
passed 258 files / 22,473 tests. The all-metric coverage report passed at
100.0% statement, branch, condition, and subroutine coverage (45,390 detail
rows; no stale uncoverable annotations). `dzil clean` removed the old 5.53
archive; `dzil build` produced 5.54 with no `cover_db` contents. The post-build
smart-router Docker guard passed 7 assertions after installing Docker CLI in a
socket-mounted disposable test container. The blank-environment integration
installed the exact 5.54 tarball using plain `cpanm` with tests enabled and
reported `Blank-environment integration run passed`. `d2 docker.images.build`
exited successfully, but its first fresh image run reported module version
5.53 rather than 5.54. The ignored local Dockerfile installs into a system
library directory while `/root/perl5` remains first in the runtime search
path. An isolated `d2 docker compose` rebuild installed the 5.54 archive into
`/root/perl5`; fresh `d2 version` and direct `Developer::Dashboard::VERSION`
probes both reported 5.54. The ignored local Dockerfile was not changed, so
future standard image builds may reproduce the shadowing until its install
target is corrected. Security scans and required targeted tests passed. Commit
`aeabf0a8` records the Problem 34 code and release updates.

Problem 34 public-helper regression (2026-10-06): a follow-up audit found that
the public `_dashboard-core docker compose` branch called `resolve()` and then
directly executed its raw multi-`-f` command. That bypassed the materialization
path in `DockerCompose::run()`, so the prior tests for `run()` did not verify
the real CLI's build/up/down path. The red-first regression in
`t/30-dashboard-loader.t` failed because the helper did not call the streaming
runner; `t/94-dockercompose-coverage.t` also failed because the method did not
exist. The helper now calls `run_streaming()` with the already resolved
command. This runner materializes the layers, keeps its temporary merged file
alive until Compose exits, inherits stdout/stderr for normal streaming, and
returns the child exit code. In the isolated `dd-problem34-red` Docker dev
container, both focused tests passed (2 files / 295 assertions). Its Docker
client is absent, so actual engine-backed build/up/down was not attempted; a
stub Compose executable verified for each action that the real operational
call uses one existing merged file, retains `--project-directory`, streams
output, and returns the operation status. `doc/problem-report.md` now records
this distinction instead of implying the prior `run()` tests covered public
dispatch.

Current-cycle final audit (2026-10-03): `t/90`, `t/91`, and `t/94` passed in
Docker (3 files / 359 assertions); the added completion defensive-branch test
passed `t/69` (71 assertions); corrected `t/590` passed 17 assertions. The full
instrumented suite passed 257 files / 22,371 tests. Initial four-metric gate
result: statement/branch/subroutine 100%, condition 99.9%, exit 1. After the
new `t/69` case ran under Devel::Cover using the same database, the aggregate
checker reported 100.0% for all four library metrics and no stale annotations.
This is a combined full-suite plus focused instrumented-test result, not a
successful clean exit from a single full gate invocation. Devel::Cover emitted
warnings about ephemeral skill fixtures deleted before report generation and
the custom database marker; these warnings remain visible and are not
described as warning-free verification.

The Markdown index `doc/problem-report.md` is maintained in numerical order;
this text file preserves the longer chronological audit. Keep status and
verification facts consistent between the two files.

Final release-gate correction (2026-10-03): after that earlier audit, the full
5.48 instrumented suite was rerun with the defensive completion assertion
included and passed 257 files / 22,372 tests. The four-metric checker exited
successfully at 100.0% statement, branch, condition, and subroutine coverage
(45,335 detail rows, no stale annotations). Devel::Cover still emitted
warnings for temporary skill fixture files removed during tests; this result
is not described as warning-free. `dzil build` produced
`Developer-Dashboard-5.48.tar.gz`; `d2 docker.images.build` completed and a
fresh image returned `d2 version` 5.48. The blank-environment Compose run then
installed the host-built tarball with `cpanm` without `--notest`, ran the
distribution test suite, and completed the integration script successfully.
The Compose project needed an explicit `--project` directory and a temporary
network override to use the existing shared network; no unrelated container
was inspected or changed. Problems 12, 32, 33, and 34 now have completed
functional, coverage, packaging, image, and blank-install evidence; commit
remains pending.

Documentation/release refresh (2026-10-03): the numbered Markdown report had
been behind the current verification state, so its Problem 12, 32, 33, and 34
statuses and completed 5.48 evidence were brought current. Because the report is
included in the release archive and the Docker image helper rejects rebuilding
an already-used version, the report refresh moved release metadata to 5.49.
All 76 library module versions and dist.ini were aligned; Changes, FIXED_BUGS,
README/POD synchronization, and the report were updated. The 5.49 full Docker
coverage run passed 257 files / 22,732 tests. All four library metrics were
100.0% (45,335 detail rows, no stale annotations). Devel::Cover emitted the
same known warnings for temporary fixture files removed before digesting.
The 5.49 archive was built, `d2 docker.images.build` completed, and a fresh
image returned version 5.49. The blank-container rerun installed that archive
using `cpanm` without `--notest`, passed its distribution suite, and ended with
“Blank-environment integration run passed.” Thus the 5.49 release gates and
problem fixes are committed; no push was performed.

Problem 35: Configure a real Dist::Zilla releaser for dzil release
Expected outcome: `dzil release` must find a real Dist::Zilla Releaser plugin
instead of exiting with “you can't release without any Releaser plugins.” The
release action must be an explicit PAUSE upload, and no credentials may be
stored in the repository.
Reproduction: run `dzil release` with the existing dist.ini. It exits before
building or uploading because the config contains build/gather plugins only
and no class with the Dist::Zilla::Role::Releaser role.
Red test: `t/15-release-metadata.t` asserted that dist.ini contains the real
`[UploadToCPAN]` stanza; before the fix this assertion failed (1 of 5,396).
Fix: configure `[UploadToCPAN]`, document that `dzil release` uploads to PAUSE,
and keep credentials in user-owned `~/.dzil/config.ini` or `~/.pause` only.
The test now passes. Host `dzil authordeps` lists
`Dist::Zilla::Plugin::UploadToCPAN`; direct Dist::Zilla configuration
inspection finds `UploadToCPAN (Dist::Zilla::Plugin::UploadToCPAN)` under
`-Releaser`; and `dzil build` succeeds. We did not execute `dzil release`,
because that would upload externally.
The initial full gate found stale 5.49 artifacts and correctly failed the
single-archive hygiene assertion. `dzil clean` was run against the prior
version configuration to clean generated artifacts; after rebuilding only
5.50, `t/36-release-kwalitee.t` passed 7/7. The clean 5.50 full Docker gate
passed 257 files / 22,733 tests and all four library metrics at 100.0% (45,335
detail rows, no stale annotations). Devel::Cover reported digest warnings for
temporary test fixtures deleted during coverage; these remain visible and the
run is not described as warning-free.
The 5.50 archive was installed in a fresh blank Docker environment with
`cpanm` and tests enabled (no `--notest`); the distribution suite and complete
integration script passed. The matching `d2 docker.images.build` completed,
and a fresh image returned `d2 version` 5.50. `dzil release` was deliberately
not run because it would upload the distribution to PAUSE.

Problem 25 and Problem 36 verification cycle (2026-10-03):

Problem 25's external-help regression was reproduced as
`d2 tira.tasklist.prune --help` being claimed by the dashboard's private
`skills _exec` help route and failing with an unknown `_exec` action. The fix
keeps help detection from taking ownership after that private dispatch
sentinel; a fake installed skill CLI verifies `--help`, `-h`, and `help` are
passed through unchanged. Problem 36's pre-fix Docker fixture had the same
skill name in project and home runtimes but completion returned only
project-local command names. The fix enumerates all discovered skill roots in
their established precedence order and emits each dotted name once; the
project layer remains the runtime winner for actual execution.

Focused Docker verification passed 4 files / 1,693 tests, including both
problem regressions. After installing the optional author-gate dependencies in
the isolated `dd-problem36` development container and setting its Git
safe-directory for the `/work` bind mount, `prove -lr t` passed 258 files /
22,959 tests. `t/158` skipped because operator-local `.claude` tooling is not
part of this checkout; Windows QEMU and Chromium-only browser tests skipped
because those optional tools are unavailable. The release-archive guard tests
also skipped in this pre-build run because the 5.52 tarball does not yet exist.
The all-metric coverage gate passed 258 files / 22,897 tests with 100.0%
statement, branch, condition, and subroutine coverage (45,356 detail rows, no
stale annotations). Devel::Cover emitted warnings for temporary fixture
modules removed before digesting; these warnings are recorded and were not
caused by the completion change. Archive kwalitee, final package tests,
blank-environment install, and image build were pending at the time of that
coverage run.

Final 5.52 package verification: `dzil clean` and `dzil build` completed.
There is exactly one root tarball, `Developer-Dashboard-5.52.tar.gz`; all 76
library modules and `dist.ini` declare 5.52, the embedded main module declares
5.52, and archive inspection found no `cover_db`. The Docker release-focused
tests passed 6 files / 6,228 assertions, including kwalitee, POD syntax,
archive inspection, both numbered regressions, and the smart-router package
guard. A fresh blank Docker install used `cpanm` with its default test behavior
(no `--notest`) and finished with “Blank-environment integration run passed.”

The required `d2 docker.images.build` was attempted against 5.52. BuildKit
failed at the Dockerfile's first `RUN curl ... | sh` with `mount options is too
long`. The wrapper then disabled the development marker, installed the tarball
into the host Perl library, and returned success; this is not a successful
image build and no new image/version claim is made. The same BuildKit failure
was recorded for 5.51. Problem 25 remains in progress, and delivery of Problem
36 is also not declared complete while the mandatory image gate remains
unresolved. No commit or push was made.

BuildKit root-cause follow-up (2026-10-03): the failed Compose context is the
local D2 image service, whose Dockerfile starts with `FROM
developer-dashboard:latest` and immediately executes the installer in a new
RUN layer. A read-only `d2 docker compose run --rm --no-deps` against that
configured base image measured 408 overlay lowerdirs; the lowerdir option
alone is 4,085 bytes and the complete `/proc/mounts` record is 4,128 bytes.
This is consistent with containerd's page-sized mount-option guard rejecting
the next overlay snapshot before the RUN command executes. The current
containerd mount implementation compacts large overlay lowerdir options
before enforcing its page limit; the local build backend still rejects the
resulting mount options after any compaction it performs. This is
environmental to the configured base-image layer stack/runtime, not to the
5.52 Perl archive. Safely reducing the base stack or upgrading/reconfiguring
the Docker/containerd runtime requires operator-level authorization; no image,
container storage, or daemon configuration was changed. The P36 regression
and package changes are still uncommitted while this required image gate is
unresolved.

Problem 25 and 36 final delivery follow-up (2026-10-03; release 5.53):

The configured base's deep layer stack was exported and flattened into a
temporary local image using an isolated D2 Compose project. The resulting
Compose image build completed. Runtime verification initially exposed a stale
5.41 copy at `/root/perl5/lib/perl5/Developer/Dashboard.pm`, which shadowed
the newly installed 5.53 module under `/usr/local/share/perl/5.40.1`. For the
disposable verification image only, that exact stale module file and directory
were removed before installing the 5.53 archive. A fresh D2 Compose run then
reported `d2 version` 5.53 and loaded `Developer::Dashboard` 5.53 from the
installed package path. The ignored local Dockerfile was restored afterward;
no daemon settings or unrelated containers were changed.

The 5.53 archive contains version 5.53 in `dist.ini` and
`lib/Developer/Dashboard.pm`, and the built image reports 5.53. The blank
environment integration flow passed using plain `cpanm` with tests enabled.
The full D2 suite then passed 258 files / 22,403 tests, including all three
P25/P36 regression files and the release metadata gate. Its optional skips
were limited to unavailable external tools/operator-local `.claude` assets.

The initial source-suite run exposed missing ignore rules. HEAD `1dc5c062`
had narrowed `.gitignore` to only `.claude/`; the worktree restores the
required `nytprof.out`, `nytprof/`, `.worktrees/`, and `dogfood-output/`
patterns while retaining `.claude/`. The 5,344-assertion release metadata
test and the subsequent full suite both pass with those rules. No commit or
push was made.

Problem 34 post-push security gate (2026-10-06): pushed commits through
`17cbbb56` and ran the required interactive-shell Scorecard command. Aggregate
score: 8.2/10. All checks tied to the code and packaging changes scored 10/10.
The remaining scores are repository-administration or historical-activity
items, not source fixes: Branch-Protection 0/10 requires GitHub branch settings;
CII-Best-Practices 0/10 requires external badge-program enrollment;
Code-Review 0/10 reflects 0 of 20 approved historical changesets; Contributors
3/10 reflects the repository's one contributing organization. No legitimate
repository-only edit can change the latter two historical facts. These four
items are recorded as external follow-ups; the Scorecard output showed no
additional repo-fixable failure.

Problem 37: False collector watchdog restart warning during cron quiet hours
(status: implementation verified in focused Docker tests; release gates pending)

Expected outcome: when a collector has a valid cron schedule whose next matching
run is hours away, a live scheduler loop with a fresh heartbeat remains healthy.
It must not be stopped, restarted, have its watchdog restart count incremented,
or be marked attention_required merely because its last job execution is old.
A cron scheduler whose own heartbeat is stale remains eligible for watchdog
recovery. Interval collectors continue to use job execution progress.

Reproduction in the isolated Docker dev service: configure
`nightly.cron` with `cron: '0 7-8 * * *'`; use a live running-loop record with a
fresh `heartbeat_at`; set collector `last_completed_at` to an old timestamp;
seed `watchdog_restart_count` at its configured limit (3) and a restart-window
start less than 300 seconds old; run one `_supervise_collectors_once` pass for
that collector. Before the fix, `t/09-runtime-manager.t` failed four assertions:
the watchdog returned an attention entry, stopped the loop, changed the count
from 3 to 4, and set `watchdog_status` to `attention_required`. In the Docker
service, `TZ=Pacific/Kiritimati date` showed 22:40 local while
`_cron_due('0 7-8 * * *', ...)` returned `not due`, confirming the reported
after-hours schedule condition.

Root cause: `_collector_stalled_for_watchdog` compared only collector-level
`last_completed_at`, `last_started_at`, or `last_run` against a short
interval-derived threshold. It did not account for cron being intentionally
idle between matching schedule minutes. The watchdog therefore classified a
healthy loop as stalled and consumed its restart budget on every subsequent
supervisor pass.

Fix: cron jobs now use the persisted loop state's `heartbeat_at` to determine
scheduler liveness. Missing or malformed cron heartbeat state does not fall
back to the old job execution timestamp, since that timestamp cannot establish
cron-loop failure during a legitimate quiet period. Non-cron collector behavior
is unchanged.

TDD and Docker verification: the regression was added first and failed in the
isolated `dd-problem37` container with the state transition above. After the
implementation, focused Docker tests passed: `t/09-runtime-manager.t` and
`t/100-runtimemanager-coverage.t`, 2 files / 1,005 assertions. The four-metric
coverage run passed the instrumented full suite (258 files / 22,497 tests) and
reported 100.0% statement, branch, condition, and subroutine coverage across
`lib/` (45,455 detail rows; no stale uncoverable annotations). The required
security web tests also passed (3 files / 459 tests). Devel::Cover emitted
warnings for temporary skill fixture files removed by tests before report
digestion; the coverage gate still completed successfully. `dzil clean` and
`dzil build` produced `Developer-Dashboard-5.56.tar.gz`; `d2
docker.images.build` exited successfully. The blank-environment integration
container installed that archive with `cpanm` tests enabled and completed the
full integration script. One separate image-version check remains noteworthy:
the built image contains `Developer::Dashboard` 5.56 under
`/usr/local/share/perl/5.40.1`, but `PERL5LIB=/root/perl5/lib/perl5` selects an
older 5.54 copy for `dashboard version`. Thus the archive install is verified,
but the existing image runtime's local-library shadowing is not claimed fixed by
Problem 37.

Problem 38: Normalize captured merged Docker Compose YAML to valid UTF-8
(status: done; release 5.57)

Expected outcome: every merged Compose file written by
`Developer::Dashboard::DockerCompose::_materialized_command` is valid UTF-8,
including when an input contributes a legacy single-byte character. The
normalized file must be used by `config`, `up`, `down`, `build`, `ps`, `logs`,
and other Compose operations through the shared materialization path.

Problem understanding and root cause: the reported `d2 yq` diagnostic is the
visible symptom, but the failing artifact was produced earlier. The resolver
captured the output from `docker compose ... config` and wrote those bytes
straight into the temporary merged YAML file. An isolated Windows-1252 pound
byte (`0xA3`) therefore remained an invalid UTF-8 octet; `YAML::XS` rejected
that generated file with `invalid leading UTF-8 octet`. The original
`/tmp/here.yml` was unavailable in this checkout, so it was not read or reused
as a fixture; the producer path was reproduced synthetically inside Docker.

Reproduction: run the Docker test `t/94-dockercompose-coverage.t` with a stub
Compose executable that emits otherwise-valid YAML containing byte `0xA3` in a
label. Before the fix, the merged file retained `0xA3`, and loading it with
`YAML::XS::Load` failed with the same invalid-leading-octet error. After the
fix, the byte is converted to UTF-8 for `£`, the output file parses, and the
parsed label retains the pound sign. The test also exercises `config`, `up`,
`down`, `build`, `ps`, and `logs` through `run_streaming` to prove they all
consume the materialized output. Existing valid UTF-8 of two-, three-, and
four-byte sequences is retained; undefined Windows-1252 octets become the
Unicode replacement character.

Verification: the final Docker run of `script/coverage-gate` passed the full
suite (258 files / 22,514 tests). The coverage gate reported statement,
branch, condition, and subroutine totals at 100.0%. The focused Compose test
also passed, including its regression for invalid captured bytes, YAML parsing,
and operational commands sharing the materialization path. The full suite
required a separate test-only adjustment in `t/680-io-failure-s7-coverage.t`:
the assertion now checks that the deliberately blocked nav layer contributes
no entry while allowing other active runtime layers to contribute. Security
review found only existing expected references in policy checks, test fixtures,
and docs; no unsafe execution, interpolation, path handling, or dependency
change was introduced. `dzil clean` followed by `dzil build` produced
`Developer-Dashboard-5.57.tar.gz`; `d2 docker.images.build` succeeded. The
blank-environment flow installed and tested the archive with plain `cpanm`
(without `--notest`) and completed its installed CLI/web checks. The reported
`/tmp/here.yml` was not read or used as a fixture.

Problem 38 source-byte follow-up: Normalize local base YAML before service
discovery (status: done; release 5.63)

Expected outcome: every Docker Compose operation runs `docker compose config`
against the effective base plus resolved overlays, normalizes the generated
YAML to UTF-8, and runs the requested verb against that materialized file.
Local source files must not be edited, and non-Compose commands remain direct.

Root cause and exact reproduction: `resolve()` called
`YAML::XS::LoadFile` on local base Compose files to determine their service
names before `_materialized_command` could normalize Compose's output. A
source `compose.yaml` containing byte `0xA3` therefore failed at resolution
with `invalid leading UTF-8 octet`, before the Docker/Compose stub was
invoked. This was a distinct path from the already-fixed case where captured
`docker compose config` output itself contained an isolated byte. The test
first wrote a raw Windows-1252 pound byte into an isolated local base and ran
`t/94-dockercompose-coverage.t` in Docker; the new assertion failed with that
exact parse diagnostic and zero Compose calls. The fix reads the source as raw
bytes, normalizes only an in-memory copy for service discovery, and leaves the
original source untouched. The red regression then verifies the operation
sequence is `config` followed by `ps -f <temporary-file>` and that the copied
materialized YAML contains valid UTF-8 `£`. The same test retains coverage for
config/up/down/build/ps/logs and confirms non-Compose commands bypass
materialization. `/tmp/here.yml` was not opened or used as a fixture.

Verification: Docker full coverage passed 258 files / 22,550 tests;
statement, branch, condition, and subroutine totals were each 100.0%, with
45,519 coverage detail rows and no stale uncoverable annotations. Focused
release/Compose verification passed 3 files / 5,965 tests, and the required
focused web-security trio passed 459 tests. The coverage run skipped optional
browser/QEMU/kwalitee tooling that is unavailable in the dev image, and
`cpan-audit` advisory-fixture coverage was explicitly skipped because that
tool is absent; neither skip failed the coverage gate. Devel::Cover printed
warnings for ephemeral fixture files deleted before report digestion; the
gate completed successfully.

The release build and image version were verified at 5.61, and the blank
environment installed that tarball with `cpanm` (without `--notest`) and
completed the full integration script. The final report and generated README
were synchronized, and the release metadata advanced to 5.63 rather than
reusing version 5.62.
Earlier Problem 38 release work also corrected an image version-shadowing
issue: the ignored operator-local D2 Dockerfile installs the archive into the
runtime-first `/root/perl5` library, and an integration assertion guards that
recipe when present. No commit or push was requested.

Problem 39: Numbering gap — no problem description recorded (status: unassigned)

The supplied problem sequence and repository records contain Problem 38
followed by Problem 40, with no Problem 39 description, capture, expected
outcome, or implementation request found. This entry records the gap without
inventing a bug or claiming a fix. If Problem 39 was assigned elsewhere, its
source report is needed before this slot can be classified further.

Problem 40: Resolve the base Compose service list before loading service overlays
Status: implementation, full tests, coverage, packaging, image, and blank-container integration passed.
Date: 2026-10-07

Expected outcome: run `docker compose config` first with the local base and
configured non-service layers. Parse that output's `services` map, then search
active home, project, and nested skill runtime roots for only those service
names. Apply `disabled.yml` and `develop.yml` marker behavior before adding
service files to the final config. Preserve explicit project-directory and
user-supplied `-f` options without calculating the operation argv position
from a number of generated file pairs.

Root cause: `resolve()` selected isolated service compose files before Docker
Compose had resolved the base config. It used raw local YAML, command-line
service inference, and an ecosystem-wide enabled service scan; consequently
the initial `config` call could contain unrelated service overlays. Then
`_materialized_command` sliced `@full` at `2 + 2 * number_of_files`, which
assumed every generated `-f` pair was a contiguous argv prefix and broke when
other compose-global options or user file arguments were present.

Reproduction steps: a new hermetic fixture is built inside the project dev
container. Its source base names `source_only` and `blocked`; a project
non-service overlay defines `present`; runtime files provide home and nested
skill layers for `present`; the `blocked` service is disabled; and `present`
has a development marker. Call `run()` with an explicit project directory,
one additional `-f` base file, and `build ghost`. The docker stub's first
`config` output lists `present` and `blocked`. The test makes
`_local_compose_services` die if execution calls it, then checks the first
invocation has no runtime service overlay, the next config contains only
matching enabled overlays (including opted-in development), and the last
operation uses the merged file while retaining its arguments and project
directory.

A follow-up red test covered a project without a conventional Compose filename
where the only base is supplied through `-f`. Before the correction, the first
config call preloaded runtime service folders and omitted the selected skill
environment; two assertions failed. `resolve()` now parses leading Compose
options to detect explicit `-f`/`--file` inputs and defers service lookup for
that case too. The fixture passes with the explicit base alone in the first
call, matching overlays in the later config, and service environment applied
only after Compose returned the service map.

Red-before-fix evidence: the first Docker run of the new regression failed
four assertions. It observed two calls rather than three; the initial config
already contained `config/docker/ghost/compose.yml` and
`config/docker/present/compose.yml`; the later config lacked the selected
`present` overlay; and the expected operation log entry was absent. This
matched the reported incorrect order. After the fix, the focused red/green
regression passed with the full resolver file. The six changed focused
regressions (`t/05`, `t/10`, `t/11`, `t/30`, `t/94`, and `t/266`) first passed
2,105 assertions; after version alignment, all seven focused files including
`t/15-release-metadata.t` passed 7,540 tests. The resolver file independently
passed 305 assertions, including the explicit-file-only base case.

Implementation: execution now passes an internal deferred-discovery flag
through the private Docker helper. `_materialized_command` explicitly splits
global compose options, explicit `-f` base files, project-directory, and the
requested operation; no file-count argv slicing remains. It runs base config,
parses the service mapping, gathers the matching service files using the
existing layered disable/development rules, then runs a final config when
service overlays were selected. Existing base/project/service/addon/mode
precedence is maintained. Compose-returned service names must be non-empty
single path segments; `.`/`..` and names containing path separators are
rejected before filesystem lookup. Dry-run remains non-executing and is
documented as a source-based preview.

Security review for this change: the command still passes argument vectors to
`system` and does not compose a shell string; config-derived service names only
select existing runtime-root paths. No new dependencies, SQL, HTTP routes,
credentials, or authentication behavior were introduced. The fresh full Docker
gate passed 258 files / 22,609 tests at 100.0% statement, branch, condition,
and subroutine coverage (45,787 detail rows and no stale uncoverable
annotations). The required focused web-security trio passed 459 tests.
`perlsec` was reviewed from the container's `perlsec.pod` with `pod2text`
because `perldoc` has no formatter. The 5.65 archive passed CPANTS kwalitee
(100%, 7/7 assertions) and POD syntax (348 source files, 355 assertions).
`dzil build` produced `Developer-Dashboard-5.65.tar.gz`; its image reported
version 5.65 in a one-off Compose run. A blank-environment container installed
the archive with `cpanm` without `--notest` and passed the full integration
script. Required security searches found no Problem 40 shell-string execution
or production use of a forbidden library; broad matches were existing audit
strings and test fixtures. Devel::Cover warnings for temporary generated
fixtures removed during tests remain visible, while the coverage gate exits
successfully with all four metrics at 100.0%.

Problem 41: Remove Docker from built-in indicators (status: done, version 5.67)

Expected outcome: core indicator refresh does not probe for the docker
executable and does not create a Docker status indicator. Docker status remains
available when the user explicitly configures a Docker collector. A previously
persisted core Docker record is removed, while customized and collector-owned
records named docker are preserved.

Root cause: refresh_core_indicators() called command_in_path('docker') and
persisted a Docker indicator at priority 20. A configured collector named
docker then shared that record, which made Docker appear first in the prompt.

Reproduction: run d2 ps1 without tmux indicator suppression. The old path
probed the Docker executable and created a whale indicator even in a runtime
without a Docker collector. In the Docker dev service, t/95 was changed first
to make the executable probe fatal; its red run exited at that probe. The test
also seeds the old built-in record and verifies migration cleanup while keeping
an explicitly configured collector-owned docker record.

Implementation removes the Docker probe and write from core refresh and adds
signature-based cleanup for the old core record only. Shared-lock cleanup
passes both focused Docker test files (1,042 assertions), preserving custom and
collector-owned statuses.
Three additional red assertions exposed missing writer-lock protection and
lock-failure reporting. Shared-lock cleanup now passes both focused Docker test
files (1,042 assertions), including layered/custom ownership preservation.
The first full run failed four tracked-document assertions because container
Git rejected the /work bind mount's ownership. A disposable test Compose file
sets safe.directory only inside the container. The affected integration asset
and indicator tests passed 491 assertions, and the full gate was restarted.

Final verification: all 258 Docker test files passed (22,645 tests); Devel::Cover
reported 100.0% statement, branch, condition, and subroutine coverage across
the library (45,841 detail rows, no stale uncoverable annotations). Its report
included missing-digest diagnostics for temporary generated helpers already
removed by their tests; the canonical gate exited successfully. The focused
web/security tests passed 459 assertions. Kwalitee, POD, and release metadata
passed 5,783 assertions, including 100% CPANTS kwalitee. The final 5.67 archive
excluded cover_db. The blank Docker environment installed the same 5.67 release
code through cpanm with tests enabled and passed the complete installed-runtime
integration runner. Perl's perlsec documentation was reviewed inside the
runtime container. No new executable, dependency, SQL, endpoint, credential,
or authentication surface was added.

Problem 42: Let skill environment values override home defaults (status: done, 5.68)

Expected outcome: matching values from a skill's `.env` or `.env.pl` override
home runtime values from `~/.d2` or `~/.developer-dashboard`. Deeper project
runtime layers keep final precedence. Non-skill environment behavior is
unchanged.

Root cause: SkillDispatcher loaded skill and skill-CLI files before loading
the whole runtime chain. The final runtime load replayed the home `.env` and
overwrote the skill's value.

Reproduction: in an isolated Docker dev container, create `foo/.env` with
`SKILL_WILL_OVERWRITE_THIS=I am foo`, `bar/.env` with
`SKILL_WILL_OVERWRITE_THIS=I am bar`, and
`~/.developer-dashboard/.env` with `SKILL_WILL_OVERWRITE_THIS=HOME`. Run
`d2 foo.bash -c` and `d2 bar.bash -c` with each command printing the variable.
Before the fix both print `HOME`; after the fix they print their own skill
value. Remove the home file and rerun, then set the key in a deeper project
runtime file and confirm that deeper value wins.

Red/green: the new skill dispatch assertion failed in Docker before the code
change (`got home-runtime`, expected `skill`). Focused skill and EnvLoader
tests pass after implementation, including home `.d2` plus
`.developer-dashboard` ordering and deeper project precedence. The full Docker
suite passed 258 files / 22,656 tests with 100.0% statement, branch, condition,
and subroutine coverage (45,874 detail rows; no stale uncoverable annotations).
The required web/security trio passed 459 tests. Devel::Cover emitted visible
missing-digest diagnostics for temporary fixtures removed during tests, and
existing negative-fixture tests print expected shell/archive diagnostics; the
run is not called warning-free. README and main POD remain synced. The required
security scans found no new forbidden library usage, sensitive information,
unsafe auth behavior, raw SQL, or missing security-header implementation;
perlsec was reviewed in the same runtime image. `dzil build` produced the sole
archive `Developer-Dashboard-5.68.tar.gz`, with no `cover_db` entry. CPANTS
kwalitee passed 7/7 checks at 100%, and POD syntax passed 348 source files / 355
assertions. The blank Perl 5.44 container installed the tarball using `cpanm`
without `--notest`, ran distribution tests, and completed the installed-runtime
integration script. `d2 docker.images.build` completed; a scoped run of its
image reported `d2 version` 5.68. No release upload was performed.

Problem 43: Keep Docker Compose skill env scoped to the selected service
(status: done in 5.71)

Expected outcome: when installed skills `foo` and `bar` each contribute a
Compose service and define the same variable in their skill `.env`,
`d2 docker compose up foo` uses foo's value and a later
`d2 docker compose up bar` uses bar's value. Operations without a selected
service continue to use env from all effective base services. Caller-exported
values keep their higher precedence under Problem 44.

Reproduction from `/tmp/capture-1476.txt`: create `foo/.env` with
`SKILL_WILL_OVERWRITE_THIS=I am foo` and `bar/.env` with
`SKILL_WILL_OVERWRITE_THIS=I am bar`; add a matching
`config/docker/<skill>/compose.yml` whose message interpolation reads that
key. Run `d2 docker compose up foo`, remove the home `.env`, then run
`d2 docker compose up bar`. Before the fix, bar's container printed
`I am foo` despite its own `.env` containing `I am bar`.

Root cause: `_materialized_command()` replaced the originally requested
services with every service returned by the base Compose config and then
loaded all corresponding skill env files into Compose's single global
interpolation environment. Since both skills set the same key, the last
enumerated skill won. A red-first Docker regression in
`t/94-dockercompose-coverage.t` reproduced the exact sequential failure. The
fix preserves the selected services, intersects them with the effective base
service set, and resolves skill env from only those services. Deferred local
base operations infer service selection from parsed operation args. If no
requested service matches the base or the operation selects none, the
established all-base-service environment behavior remains. Compose file and
dependency gathering is unchanged.

After the fix, `d2 docker compose exec dev prove -lv
t/94-dockercompose-coverage.t` passed 317 assertions. This covers the exact
foo-then-bar sequence, deferred selection, invalid service fallback, and
all-service operations. The full Docker suite passed 258 files / 23,044 tests.
The isolated four-metric coverage gate passed at 100.0% statement, branch,
condition, and subroutine coverage (46,002 detail rows, no stale uncoverable
annotations). The required web/security trio passed 459 tests. The successful
gate output includes existing Devel::Cover diagnostics for temporary fixtures
removed by tests and expected negative-archive diagnostics. Security review:
ASVS V1/V11 cover service-scoped interpolation and selection; V5/V12/V14 cover
validated service identifiers, service-specific environment file discovery,
and precedence; V7 preserves visible failures. V2/V3/V4/V6/V9/V13 are untouched
(authentication, sessions, authorization, cryptography, transport, and web
APIs). V8 adds no secret persistence or logging path. V10 command construction
is unchanged and uses Compose argv rather than a shell. OWASP A03/A04/A05/A08/A09
were considered; no new injection, access-control, deployment-security,
integrity, or logging surface was added. Perl's `perlsec` documentation was
reviewed in the D2 container. Required source scans found no new production
forbidden-library, credential, SQL, redirect, traversal, or header issue;
broad matches were in fixtures and documented scan commands. The web/security
trio passed 459 tests.

`dzil clean` preceded the 5.71 bump. `dzil build` produced only
`Developer-Dashboard-5.71.tar.gz`, which contains no `cover_db`. Release
metadata passed 5,428 assertions; CPANTS kwalitee passed 7/7 indicators.
The exact archive installed in a blank Perl 5.44 container using plain
`cpanm` (without `--notest`), ran the packaged distribution tests, and ended
with `Successfully installed Developer-Dashboard-5.71` / `121 distributions
installed`. `d2 docker.images.build` succeeded, and an isolated run of the
image reported version 5.71. The optional t/44 guard skipped because the dev
container has no nested Docker CLI; the direct blank-container install-and-test
flow above passed. No `OLD_CODE` file was changed.

Problem 44: Preserve explicit command-line environment overrides
(status: done in 5.70)

Expected outcome: `NAME=value d2 <skill>.<command>` passes `value` unchanged to
the command, even when home and skill `.env` or `.env.pl` files define `NAME`.
When the caller leaves a key unset, normal file precedence remains: skill
settings override home defaults and deeper project runtime settings override
skill settings. The same rule applies to ordinary dashboard commands and skill
commands.

Reproduction from `/tmp/capture-1747.txt`: run a disposable skill command that
prints `SKILL_WILL_OVERWRITE_THIS` with a home runtime `.env` setting it to
`HOME`, then invoke it as `SKILL_WILL_OVERWRITE_THIS=here1 d2 foo.bash`. Repeat
for a second skill and remove the home file before invoking with `foo3` and
`foo4`. Before the fix, the home value replaced both explicit assignments
(`here1`/`here2`), and then each skill value replaced explicit assignments
(`foo3`/`foo4`). The capture also confirmed that ordinary skill values should
remain active when no caller override is present.

Root cause: the public switchboard loaded runtime env files directly into
`%ENV`, replacing inherited caller values before it exec'd a helper. Skill
dispatch then loaded skill env files through `_load_skill_layer_specs()`, a
separate code path that bypassed `load_files()` and replaced those caller
values a second time. The two loaders also left env-audit provenance claiming
that a file supplied a value that actually came from the invoking process.

Red tests: `t/87-envloader-coverage.t` first failed with `from-file` instead of
`from-caller`; `t/30-dashboard-loader.t` exercises the public switchboard and
dotted skill command in an isolated home inside the Docker development
container. Tracing the first partial fix showed that the skill-dispatch test
still failed because `_load_skill_layer_specs()` independently ran each skill
file. The final correction preserves caller values at both loading
boundaries. Focused Docker verification passed 2 files / 216 assertions. It
also verifies unset keys still populate from files, skill values still
override home defaults, error paths restore caller values, and errors remain
visible.

The full Docker suite passed 258 files / 22,690 tests. The all-metric coverage
gate reported 100.0% statement, branch, condition, and subroutine coverage
(45,961 detail rows; no stale uncoverable annotations). The required
web/security trio passed 459 tests. Devel::Cover emitted visible missing-
digest diagnostics for temporary fixture sources removed during tests, and
negative-fixture tests printed their expected shell/archive errors; the run is
not called warning-free. Static security scans found no new forbidden
libraries, auth/redirect/header issue, raw SQL execution path, or secret in the
change. The `perlsec` guidance was reviewed against this environment handoff:
key names are validated, actual caller values remain in `%ENV`, and no
untrusted value is interpreted as shell syntax or a path.

Release verification: CPANTS kwalitee passed 7/7 indicators (100%), and the
POD syntax gate passed all 348 discovered Perl source files. The final 5.70 tarball
passed `dzil build` and contains no `cover_db`; `cpanm` installed and tested it
without `--notest` in a blank Perl 5.44 Compose container, whose installed-
runtime integration flow passed. Headless Chromium printed missing-D-Bus
diagnostics and the expected unauthenticated-route response during browser
checks; assertions passed. `d2 docker.images.build` completed, and a scoped
Compose run reported `d2 version` 5.70. An earlier 5.69 image had already been
built in this cycle; the final report therefore uses a distinct 5.70 version.
No release upload was performed.

Problem 45: Add Docker-style timestamps, follow, and tail to dashboard logs
(status: done in 5.77)

Expected outcome: `d2 logs` supports Docker-style `-t`, `-f`, `--tail N`, and
`--tail=N` across combined web/collector, web-only, all-collector, and named-
collector views. Flags combine: tail limits the initial snapshot and follow
streams later additions. `-n N` remains compatible; zero returns no initial
lines; invalid and negative counts produce usage text. Collector entries use
their persisted run timestamp; raw web log lines are timestamped at read time
because the web log file has no per-line event-time metadata.

Red-first reproduction: in the isolated Docker fixture, seed web and collector
entries and run the `--tail=2` form, both tail spellings, `-t`, and `-f` for
each scope. Before tail support, `--tail=2` returned all output; before the
shared timestamp/follow fix, the flags were not consistently supported across
scopes and `dashboard serve logs`. Regressions cover timestamp prefixes,
persisted collector times, append following, truncation, and mixed sources.

Root cause: the runtime-control parser exposed only `-n`, while web follow used
a blocking reader and could not poll collectors alongside web logs. The fix
centralizes timestamp/follow/tail parsing, composes sources before applying
tail, polls selected sources independently, and routes `dashboard serve logs`
through the shared parser. Verification: 258 files / 23,151 tests passed;
coverage was 100.0% statement, branch, condition, and subroutine (46,174 detail
rows, no stale annotations). The focused runtime-control test passed 141
assertions; help-dispatch passed 469; release metadata 5,427; POD syntax 348
files; and the web-security trio 459 tests. `perldoc perlsec` and the required
security scans were reviewed, with no new production findings or dependencies.
`dzil clean`, `dzil build`, and `d2 docker.images.build` succeeded for 5.76.
The isolated 5.76 image reported `d2 version` 5.76 and `d2 logs --help` listed
`-t`, `-f`, and both tail forms. Plain `cpanm` without `--notest` installed the
archive in a blank Perl 5.44 container, passed package tests, and completed
installed-runtime integration. The archive contains no `cover_db`; the
temporary integration Compose project was removed. Problem 44 already records
environment precedence, so this issue remains Problem 45. The change was
committed and pushed; post-push Scorecard reported an 8.1/10 aggregate, with
remaining external project settings, review history, contributor makeup, and
badge enrollment items.

Problem 46: Start path-alias workspaces inside Docker
(status: container config regression fixed 2026-10-10)

Expected outcome: `d2 workspace -c <alias> --docker` and `-d` resolve the
registered alias, change to its directory, run `d2 docker compose up -d --build
workspace`, then add the alias at `/workspace` inside the container and start
`d2 workspace <alias> -c` there. Both in-container commands use
`DEVELOPER_DASHBOARD_CONFIG_OVERLAY=/dev/shm/developer-dashboard-workspace-config`
so the host configuration is not changed. If startup fails or the Compose workspace
service is missing, report how to configure it and do not run either exec
command. Without Docker mode, retain the local tmux flow.

Reproduction: register an alias to a project, then run both Docker flag forms.
Before the fix, workspace recognized only `-c` and always attached to host tmux.
Also verify `-d` without `-c`, a raw absolute path, and a Compose project with no
`workspace` service.

Root cause: the workspace parser stripped only `-c` and unconditionally entered
the local tmux implementation; there was no ordered Compose orchestration or
failure guard before container commands.

Red tests: `t/91-cli-ticket-coverage.t` verifies command ordering, directory
selection, invalid arguments, and early exit at startup/exec failures.
`t/266-cli-help-dispatch.t` and `t/39-cli-suggest-complete-coverage.t` cover
help and completion. The new orchestration assertions failed before code was
added.

Fix: the workspace parser accepts `-d`/`--docker` with `-c`, resolves and
changes into the registered alias directory, then invokes public Compose with
an argument list (no shell). It starts the `workspace` service, registers the
alias at `/workspace`, and starts the normal inner workspace in that order;
every failed stage stops later commands. Omitting Docker mode keeps local tmux.

Verification: inside the `dev` container, 258 test files and 23,201
tests passed; statement, branch, condition, and subroutine coverage were each
100.0%. Required web/SSL gates passed (3 files, 459 tests). A real checkout
invocation against the repository Compose config failed at the expected first
stage because only `d2` and `dev` are configured; it printed setup guidance and
ran neither in-container command. Injected-runner tests verify exact successful
ordering and each early-failure path. An initial blank-container package run
exposed a test-fixture issue: without an init reaper, process-group tests could
observe orphaned children as zombies. After recreating the isolated Compose
service with `init: true`, plain `cpanm` (without `--notest`) installed both the
5.78 package and final 5.79 archive; each passed the packaged suite (128
distributions installed). The 5.79 image built successfully and reports `d2
version` 5.79. The final archive includes the completed report; runtime code is
unchanged from 5.78. The repo does not configure a `workspace` service, so only
the real service-absent path could be run; a successful interactive session is
unavailable here, while exact success order is covered by runner tests.

Post-push Scorecard (2026-10-10) reports 8.1/10: Branch-Protection 0/10
(`master` is unprotected), CII-Best-Practices 0/10 (no OpenSSF badge enrollment),
Code-Review 0/10 (0/30 approved changesets), Contributors 3/10 (one detected
organization), and CI-Tests unknown (no PR found). Source-side checks scored
10/10. These remaining gates require repository administration, external badge
enrollment, or genuine review/contributor activity and are not Problem 46 code
failures.

Regression reported 2026-10-10: with host alias `somewhere` pointing to
`/tmp/foobar`, the in-container `d2 path add somewhere /workspace` inherited a
bind-mounted host config directory and rewrote the host alias. Expected: host
`d2 paths` keeps `/tmp/foobar`, while only the in-container alias resolves to
`/workspace`.

Red test and root cause: the first updated `t/91-cli-ticket-coverage.t` run
failed because both Compose exec commands lacked a private config-root
environment. The regular config writer then selected the mounted host config.

Fix: both container exec commands now pass
`DEVELOPER_DASHBOARD_CONFIG_OVERLAY=/dev/shm/developer-dashboard-workspace-config`,
so `path add` and the nested workspace command share an additive container-local
config layer. The overlay directory is owner-only and symlinks are rejected.
The updated command test asserts this exact environment on both Compose exec
commands; it was red before the fix and green afterward. The Docker run
`d2 docker compose exec dev prove -lv t/97-pathregistry-coverage.t
t/91-cli-ticket-coverage.t` passed all 425 assertions. The path-registry tests
verify overlay precedence, preservation of the host alias at `/tmp/foobar`,
owner-only permissions, and symlink rejection. A container smoke check stored
`somewhere` as `/workspace` with `d2 path add`, then resolved it back to
`/workspace`. The temporary container test directories were removed after
verification. This repository has no `workspace` service or bind-mounted host
home fixture, so an interactive workspace session could not be exercised.

Problem 46 follow-up reported 2026-10-10: when `d2 workspace -c <alias> -d`
finds the workspace container already running, it must not rebuild/replace it
and risk losing in-container progress. When the service is stopped, Compose
must retain its default build cache so resume/start is not needlessly slow.

Expected outcome: inspect only the `workspace` service state. If it is running,
reuse it and proceed directly to the private-overlay alias registration and
inner workspace command. If it is stopped, run `up -d --build workspace`
without `--no-cache`, preserving the normal Docker layer cache. If its state
cannot be queried, fail visibly before any mutating or in-container command.

Red-first regression: `t/91-cli-ticket-coverage.t` initially failed on the
missing state probe, the unconditional `up --build` while running, the stopped
service command order, and the absent cache/error assertions.

Fix: the orchestration now runs `compose ps --status running --services
workspace` first. It skips the build for a running service; a stopped service
uses cached `up -d --build workspace`. The default runner captures probe output
for service matching and keeps normal successful command output visible. A
failed probe stops with actionable details. The focused test passed inside the
`dev` container (242 assertions), including a fork-isolated failed `exec tmux`
path and an undefined child-status error path that must not emit a Perl warning.
The full Docker coverage gate passed 259 files and 23,290 tests; statement,
branch, condition, and subroutine metrics are each 100.0%, with no stale
uncoverable annotations.

Release verification for the P46/P47 changes: `dzil clean` removed the prior
5.82 build, then `dzil build` produced only `Developer-Dashboard-5.83.tar.gz`.
All 76 library modules, `dist.ini`, the main POD, and README report 5.83; the
archive contains the P46/P47 regression tests and no `cover_db`. The isolated
`d2 docker.images.build` completed, and a one-off Compose run returned
`d2 version` 5.83. A separate blank Perl 5.44 container installed the archive
with plain `cpanm` (tests enabled, no `--notest`) and the packaged suite passed;
the full blank-environment integration runner then reported success. Its
uniquely named Compose project was brought down afterward. GitHub's
multi-platform publication and the required post-push Scorecard run remain
pending the verified commit and push.

Problem 47: Publish current master builds to Docker Hub
(status: locally complete; GitHub publication pending)

Expected: a master push or manual dispatch builds the checked-out current
source, runs install.sh, creates a Dist::Zilla archive, installs and tests that
archive with cpanm, and publishes both the X.XX version tag and latest to
Docker Hub. The image supports Linux amd64/arm64; Apple Silicon uses Linux
arm64 under Docker Desktop, not a macOS-kernel container.

Cause: install.sh bootstraps from MetaCPAN, which may lag behind fixes on
master. No image workflow replaced that installed CPAN package with a tarball
built from the checked-out source.

Red-first regression: t/228-dockerhub-image-workflow.t checks secret wiring,
the master/manual triggers, tag derivation, multi-platform build, and the
install.sh -> dzil build -> cpanm sequence. It also guards the shared Docker
context exception needed to retain the archive for d2 docker.images.build. It
skips from source archives because Dist::Zilla intentionally excludes .github
workflow assets.

Implementation: a pinned-action workflow in `.github/workflows/dockerhub-image.yml`
uses the `release` environment, validates the distribution version, and pushes
versioned and `latest` tags for Linux amd64/arm64. The Dockerfile bootstraps
through `install.sh`, builds the checked-out source with Dist::Zilla, then
installs/tests that exact archive. `.dockerignore` excludes local state and
secrets; README, main POD, integration plan, Changes, FIXED_BUGS, and this
problem report document the behavior.

Verification: the Docker dev-container full suite passed (259 files, 23,312
tests) with statement, subroutine, branch, and condition coverage all 100%.
The image build and its plain cpanm archive installation/test passed (259
files, 16,985 tests); the image smoke test returned `d2 version` 5.81. The
focused release/security gate passed (8 files, 6,438 tests), including
kwalitee, POD syntax, and CPAN audit checks. Perl security guidance was reviewed
and repository security scans passed. GitHub secrets are only available to
the `release` environment, so Docker Hub publication and remote multi-platform
manifest verification remain pending the workflow running after push/manual
dispatch. The first local image attempt exposed that `.dockerignore` excluded
the archive required by the repository's d2 image Dockerfile; the new
regression assertion failed, then an exception rule fixed it. The local D2
project-layer `.env.pl` had selected the prior build marker as `D2D_VERSION`;
providing that marker as `D2D_LAST_BUILT_VERSION` made it choose the current
5.81 archive without modifying local environment configuration. The corrected
`d2 docker.images.build` completed, and a fresh isolated container returned
5.81. Host `dzil clean` then `dzil build` completed for 5.81; archive inspection
confirmed the workflow contract test is included and no coverage database is
packaged. README/POD parity and all 76 library module versions were verified.

Post-push Scorecard found the Dockerfile's Ubuntu tag was not digest-pinned
(Pinned-Dependencies 9/10). t/228 now requires the Ubuntu reference to include
an immutable manifest digest; it failed before the Dockerfile change and
passed afterward. The base now uses Scorecard's reported multi-platform
manifest digest. This corrective follow-up is version 5.82. Rerun Scorecard
after the next push and record the remaining external governance checks. The
5.81 push started the Docker Hub workflow; its completion and the 5.83
multi-platform run remain to be verified after the package and local image gates
pass.

Security review for Problems 46-47 (ASVS 5.0): V1, V4, V5, V7, V8, V10, V11,
V12, and V14 were applicable. The workspace service name is fixed, user aliases
remain argv values rather than shell source, the private overlay rejects
symlinks and non-owner directories, and startup/exec errors remain visible.
The image workflow requests read-only repository contents, pins every external
action to a full commit SHA and the Ubuntu base to its multi-platform digest,
and passes Docker Hub credentials only to the login action from the `release`
environment; credentials are not build arguments or context files. V2, V3, V6,
V9, and V13 (authentication, sessions, cryptography, transport, and application
API) have no changed runtime surface. OWASP A03, A04, A05, A08, and A09 were
reviewed for command construction, container reuse, workflow configuration,
source/image integrity, and visible failure reporting. A01, A02, A06, A07, and
A10 do not gain a changed access-control, cryptography, dependency, identity,
or request-forgery surface. Required security scans and web/static/SSL tests
passed in Docker; post-push Scorecard governance findings are tracked separately
from code-side security controls.

Problem 47 timeout correction (2026-10-10; release version 5.84)

Expected outcome: the GitHub multi-platform publish completes inside its
120-minute job limit and installs the current tarball into both runtime image
architectures. Full test and coverage gates still run before packaging; image
assembly must not repeat the full suite under QEMU.

Reproduction and root cause: pushed commit aed8ab8d triggered Actions run
38078551920. Setup, checkout, version validation, QEMU/Buildx setup, credential
validation, and Docker Hub login passed. The image build stayed in progress
until the 120-minute timeout cancelled it. The Dockerfile's final cpanm
installation had no --notest, rerunning the 23,000+ test suite for each
emulated target architecture despite separate full repository and coverage
gates.

Red test: t/228-dockerhub-image-workflow.t was first updated to require
cpanm --notest --reinstall for installing the generated archive. In the Docker
dev service it failed against the old Dockerfile (31 passed, 1 failed),
reproducing the duplicate suite.

Fix: install the newly built, version-matched tarball with cpanm --notest
--reinstall in the runtime image. Full repository and coverage gates remain
mandatory, and the blank-container integration gate installs the archive with
tests enabled. The contract test then passed all 32 assertions in the Docker
dev service. The post-fix 5.84 full suite passed in Docker (259 files, 23,284
tests); statement, branch, condition, and subroutine coverage each measured
100.0%. The focused release/security/workflow gate passed 5,625 tests, and the
required web/static/SSL gate passed 459. dzil clean and dzil build produced
only Developer-Dashboard-5.84.tar.gz; archive inspection confirmed 5.84,
inclusion of workflow/release tests, and no cover_db. The standard local image
build and a fresh one-off container both returned d2 version 5.84. A direct
local build of the workflow Dockerfile also completed and its one-off image
returned 5.84. Finally, the blank Perl 5.44 Docker environment installed that
archive with cpanm tests enabled and completed the integration harness. The
corrected 5.84 GitHub publish and post-push Scorecard remain pending this
version's push.

Problem 46 follow-up: tmux cannot start in the Docker workspace (2026-10-11–12; runtime verified, packaging gates pending)

Expected outcome: `d2 workspace -c <alias> -d` attaches the caller's terminal to
a live tmux session inside the Compose workspace container. Tmux uses a socket
directory writable by the container user, and a later workspace command
reconnects to that same session.

Reproduction: in a uniquely named Compose project, map host alias `foobar` to
the checked-out project and run `d2 workspace -c foobar -d` with the workspace
service running. Before the fix, tmux reported `couldn't create directory
/tmp/tmux-1000 (Permission denied)` after alias registration. The container ran
as uid 1000; bound `/tmp` was mode 0755, owned by root, and not writable, while
`/dev/shm` was mode 1777 and writable. In addition, the final Compose exec was
captured instead of inheriting the interactive terminal.

Root cause: the configured service's `/tmp` bind mount does not guarantee the
standard writable sticky-bit permissions, so tmux cannot create its default
`/tmp/tmux-<uid>` socket. The first outer-flow repair selected `/dev/shm` only
inside the new library code and did not pass `TMUX_TMPDIR` across Compose exec;
the exact test exposed that the container still had d2 5.84. The outer Compose
runner also captured the final long-lived attach rather than passing terminal
streams through.

Red tests: the new `t/91-cli-ticket-coverage.t` assertions failed in the Docker
`dev` service because interactive output was captured and the final stage was
not marked interactive. Socket-directory tests failed before the selector was
implemented.

Fix: the final exec requests `-it`, uses direct, uncaptured stdio, and passes
`TMUX_TMPDIR=/dev/shm` explicitly into the container-side workspace process.
Tmux commands honor explicit `TMUX_TMPDIR`; when unset and `/tmp` is
unwritable, they consistently select writable `/dev/shm`. Both the command
runner and interactive exec handoff use this selector. If neither location is
writable, the current helper reports the failure. Startup behavior is opt-in:
`d2 workspace foobar -d -c` starts a stopped service with
`up -d --no-build workspace`, preventing Compose from implicitly building an
absent image. Uppercase `-C` requests `up -d --build workspace`; that
stage runs without capture so Compose build progress is visible as it happens.
An already-running service is reused even when `-C` is passed, preserving
in-container work.

Verification: the updated `t/91-cli-ticket-coverage.t` passes 273 assertions
in Docker; related help/completion/dispatch tests pass too (3 files, 1,898
assertions). The exact outer `d2 workspace foobar -d -c` command failed before
the env handoff was added, then passed afterward in isolated project
`dd-p46-tmux-red`; tmux reported `foobar attached=1 cwd=/workspace`, with its
socket under owner-only `/dev/shm/tmux-1000`. A later `-C` on that already
running service reused it without building and reattached. The temporary host
alias still resolved to `/tmp/foobar`, and normal host config was unchanged.
The stopped-service build argv and direct progress streaming are covered by
tests; the live check did not rebuild the running workspace.

The complete Docker suite passed 259 files and 22,964 tests. The coverage gate
returned branch 99.9% and condition 99.9%, so 100% coverage is not claimed.
Version 5.87 packaging, security checks, and image verification remain pending.

Final follow-up verification (2026-10-12): host `d2 version` reports 5.84;
the checkout reports 5.88. This confirmed the original failing invocation used
stale outer code that did not pass `TMUX_TMPDIR`. With the checkout loaded via
`PERL5LIB`, a fresh stopped service started without BuildKit output and the
caller attached to the container tmux session. The read-only tmux query
reported `foobar attached=1 cwd=/workspace`, with its socket in
`/dev/shm/tmux-1000`. Repeating with `-d -C` while running reused the service
without rebuilding. Default start now uses `up -d --no-build workspace` to
prevent Compose's implicit image build. The focused test passes 278 assertions;
the full Docker suite passes 259 files / 23,026 tests, and the coverage gate
passes statement, branch, condition, and subroutine at 100.0% (259 files /
22,969 instrumented tests). `dzil clean` and `dzil build` produced only
`Developer-Dashboard-5.88.tar.gz`; blank Docker integration installed it with
`cpanm` without `--notest` and passed. Version 5.88 still needs to be installed
on the host before its unqualified `d2` command (currently 5.84) will use this
fix.

Final SDLC verification (2026-10-12): this supersedes the preceding pending
status. `dzil clean` and `dzil build` produced only
`Developer-Dashboard-5.88.tar.gz`; archive metadata and all 76 modules under
`lib/` report 5.88. README matches the canonical main POD, and archive
inspection found no `cover_db` entries. The blank Perl 5.44 Docker environment
installed the tarball with plain `cpanm` (tests enabled); the full package test
suite and integration runner passed. The required `d2 docker.images.build`
completed, and a uniquely named one-off container returned `d2 version` 5.88.
The build helper's Telegram notification timed out with HTTP 500, but this did
not prevent the image build or its host-side `cpanm --notest` install. The
development marker was removed afterward, and the host `d2 version` now reports
5.88. README/POD synchronization, security scans, archive hygiene, and
`git diff --check` passed. The Problem 46 changes are ready to commit; no push
or PAUSE release was requested.

Problem 48: Profile MCE and FFI::Platypus acceleration (implementation and
release verification in progress). Docker CLI profiling found startup and
filesystem work rather than arbitrary CPU-heavy subroutines. A native OpenSSL
FFI PBKDF2 prototype measured a 9.5x improvement (284 ms Perl median versus
29.9 ms native median) at the unchanged 210,000-round work factor. The adapter
preserves digest bytes, £ handling, and the old Digest::SHA fallback when
libcrypto is unavailable or input cannot cross the byte FFI boundary. Focused
adapter, auth, and runtime tests have passed in Docker; expanding the adapter
tests also covers argument guards, bounded integer lengths, short output
buffers, and native-call errors. MCE's synthetic readiness model is faster,
but applying it to real collector startup violates the existing start-then-
verify fail-fast sequence, so no MCE runtime dependency or code was retained.
The exact tests and measurements are in `doc/problem-48-performance-research.md`.
Full suite, 100% coverage, security, integration, packaging, image, and commit
gates remain pending.
The request was expanded to require at least 50x acceleration for every
subroutine using MCE, FFI::Platypus, Inline::C, XS, SPVM, Memoize, and Perl
class/method/field/signatures. Lazy loading removed unnecessary Platform,
InternalCLI, Handle, and FileRegistry startup work from `dashboard version`;
NYTProf confirms those modules are absent on that path. The 15-run median is
35.114 ms versus the 5.88 baseline of 56.7 ms (~1.62x). The earlier Pod::Usage
change alone measured 49.571 ms (~1.14x), with 1,029 CLI smoke tests passing.
Inline::C/XS measured 31.0 ms versus 32.4 ms FFI on the same PBKDF2 call. SPVM
was installed for isolated evaluation but no dashboard routine speedup was
proven. MCE cannot parallelize the required fail-fast collector sequence.
The v5.88 HEAD tree has 1,624 named sub declaration lines under
lib/Developer/Dashboard; the current worktree has 1,648 by the same source-line
count. Anonymous closures are not included. Selected benchmark results do not
constitute a 50x proof set for every routine.
The measured bare Perl process floor is 1.032–1.268 ms; a 50x result from the
56.7 ms baseline would require about 1.134 ms for the entire `dashboard version`
command. A profiled external `pwd` subprocess was replaced with device/inode-
validated PWD reuse across production call sites. Thirty Docker calls measured Cwd::cwd at 2.939 ms each
versus 0.003 ms for validated PWD/stat; valid symlink PWD and stale-PWD fallback
are covered. The initial full suite exposed stale test mocks and a missing
PathRegistry getcwd shim; both were fixed. A full Docker suite passed 262 files
/ 23,422 tests. The latest 15-run medians are 31.486 ms for `version`, 71.309 ms
for `paths`, and 83.641 ms for `ps1`, versus recorded 5.88 baselines of 56.7,
93.3, and 109.5 ms (about 1.80x, 1.31x, and 1.31x). A new red-first test found
three runtime metadata writes in the aggregate path inventory; it now writes
once. The passwd fallback also no longer imports Platform. The latest canonical
four-metric coverage run passed the suite but exited 1 at 99.8% condition
coverage. Devel::Cover emitted “can't locate structure” diagnostics for staged
private helper scripts outside `lib/`; warnings remain under review. Security,
integration, warning disposition, build/install/image gates, and commit remain.
Problem 48 remains in progress; the literal 50x target is not met.

Problem 48 follow-up (2026-10-12): the full isolated Docker suite passed 262
files / 23,440 tests. The canonical coverage gate reports 100.0% statement,
branch, condition, and subroutine coverage across 46,723 detail rows, with no
stale uncoverable annotations. A red-first gate test reproduced Devel::Cover's
missing-source digest warnings for helper trees removed by test cleanup. The
gate now excludes only disposable /tmp and staged .developer-dashboard source
paths from instrumentation; all lib/ sources remain measured. Those warnings
are gone. A separate report read emits two Devel::Cover "ignoring extra
statement" diagnostics; source records are still being traced and these were
not suppressed. Seven files skipped for unavailable environment/tooling
(cpan-audit, operator-local tool specs, QEMU, browser loopback, kwalitee,
Docker daemon). Other release, security, integration, image, blank-install,
and git gates remain. No push or PAUSE release requested.

Problem 48 follow-up (2026-10-12): tracing mapped the extra-statement counts
to saved-Ajax and CODE sandpit source generated by PageRuntime.pm. These eval
strings now carry Perl eval source identities so their dynamic statements are
not charged to the static module structure. The coverage checker now rejects
Devel::Cover warnings, even alongside a 100.0% total. Focused Docker runtime
tests passed (394 assertions); targeted PageRuntime coverage reported 0
diagnostic stderr. The full suite and coverage gate must be rerun on this tree.
Problem 48 remains open; no 50x-per-subroutine evidence exists.

Problem 48 verification update (2026-10-12): the final isolated Docker
coverage gate passed 262 files / 23,442 tests and 100.0% statement, branch,
condition, and subroutine coverage (46,727 detail rows; no stale annotations).
The report produced no Devel::Cover warning diagnostics. Runtime-generated
saved-Ajax and CODE sandpit source is mapped to eval identities instead of the
static PageRuntime.pm file, and the checker rejects warning diagnostics even
when totals report 100.0%. Seven files skipped for unavailable audit/operator
tools, QEMU, browser loopback, kwalitee, or Docker daemon. Other security,
integration, packaging, image, blank-install, version alignment, and git
delivery gates remain. The full 50x-per-subroutine target remains unmet.

Problem 48 new route under investigation: a resident Perl command runtime can
avoid restarting Perl for repeated requests. In Docker, the current `dashboard
paths -o json` command averaged 84.412 ms over ten fresh launches. The same
real handler in a resident context averaged 1.018 ms while recomputing the
path map and JSON, with byte-for-byte output parity (82.9x experiment-level
reduction). A Unix-socket prototype with a separately launched C client
averaged 0.514 ms over 100 requests and matched the CLI output (164x in that
experiment). Linux `inotify` prototypes detected config replacement and
nested-skill creation; probing an unchanged event descriptor averaged 0.0010
ms/request for 401 watches, installed in 0.532 ms. These are separate
prototypes, not a combined or production benchmark. The event-driven cache is
not implemented; overflow/recovery, cross-platform support, deletion and
symlink behavior, arbitrary user `Folder.pm` inputs, and end-to-end freshness
and parity remain open. This is a credible 50x route for repeated path
inventory requests, not proof for all commands or all 1,648 current named
subroutines.

Problem 48 fresh-process timing recheck (2026-10-12): after two warm-ups, 15
successful Docker executions measured medians of 36.379 ms for `dashboard
version`, 82.020 ms for `dashboard paths -o json`, and 90.429 ms for
`dashboard ps1`. Exit statuses were checked and stdout was discarded to keep
terminal output out of the timing. These results do not establish a 50x result;
the resident-runtime and socket figures above are separate prototype results.

Problem 48 current path-helper NYTProf map (2026-10-12): profiling the public
switchboard itself ended at its intentional exec and left an incomplete trace.
Profiling source helper entrypoint share/private-cli/paths produced a complete profile. It
counted 303 runtime-root entries, 97 skill disable-marker checks, 32 queued
top-level skills, and 152 nested skill directory entries. These are profiler
call counts, not uninstrumented timings. Skill discovery loads arbitrary user
Folder.pm code and calls its methods, so caching those results without a
complete invalidation contract could make aliases stale or alter user code.
A red-first experiment memoized skills_roots per runtime-root set, but
alternating Docker timing batches were noisy and did not show a stable
end-to-end gain. Its code and test were removed rather than retaining an
unproven optimization; work remains focused on reducing cold-start cost while
preserving filesystem and user-module behavior.

Problem 48 targeted security follow-up (2026-10-12): Perl 5.40.1 perlsec
guidance was reviewed inside the container after the focused gate. The FFI
adapter validates scalar/byte inputs, bounds values for OpenSSL's signed-int
ABI, keeps buffers live through the synchronous call, and surfaces native
errors. It adds no shell execution or user-controlled executable path. The
required web-security trio passed in Docker (3 files / 459 tests). Full ASVS
V1-V14 review and repository-wide security scans remain pending.

Problem 48 alternative compiler research (2026-10-12): B::C avoids parsing at
startup but does not improve runtime execution; B::CC documents behavior
differences. XS::JIT compiles hand-written C into XS rather than translating
Perl functions, and its published synthetic example reports 1.36x over
Inline::C. These do not establish a 50x, behavior-preserving solution.

Problem 48 isolated status-route follow-up (2026-10-12): with all account-name
environment variables unset, NYTProf measured 69.7 ms/request and 80 passwd
fallback calls across 20 Dancer/PSGI /system/status requests. Caching the
account name for the exact unchanged environment reduced the same profile to
58.3 ms/request and one fallback call. The focused PathRegistry Docker suite
passed 225 tests. This is an instrumented edge-environment result, not a
typical-route or uninstrumented claim. Separately, direct app.psgi startup
failed because Prompt->new omitted its required indicators argument. The fix
now constructs an IndicatorStore and shares it with Prompt. A red-first Docker
test failed before the fix and passes all 3 assertions, including a status
request through the actual PSGI coderef. NYTProf measured 2.62 ms/request on
20 such requests; no pre-fix timing exists because that app did not start.

Problem 48 state metadata follow-up (2026-10-12): NYTProf showed repeated
indicator-root resolution rewrote identical runtime.json metadata. A red-first
test verified the repeat write before the fix. The writer now skips only an
identical regular, owner-owned mode-0600 metadata file; missing data is
recreated, stale or loose data is repaired, and symlinks/non-regular files are
refused. Docker tests pass: 235 PathRegistry assertions and 3 actual PSGI
bootstrap/status-route assertions. On 20 requests through app.psgi, instrumented
status-handler time fell from 2.62 ms to 0.616 ms/request (4.25x); the metadata
writer fell from 1.13 ms to 0.132 ms/call (8.6x). An unprofiled paired ABBA
comparison of reconstructed legacy and verified writers in the same process
measured 1.044 ms versus 0.587 ms/request across 80 requests each, with exact
response-body parity (1.78x). This isolates the writer change, is not a v5.88
benchmark, and does not prove the 50x all-routines target.

Problem 46 follow-up: UTF-8 prompt icons render as underscores inside Docker tmux (2026-10-12)

Expected: the Docker workspace prompt and tmux status preserve the host's
Unicode icons. The recording shows the ticket marker and status icons as
underscores inside the container. The checkout's `d2 ps1` output contains
valid UTF-8 bytes, so the prompt renderer does not corrupt them. In the
workspace image, `LANG` and `LC_CTYPE` are unset and `locale` reports POSIX.
An isolated Docker reproduction using tmux and an attached pseudo-terminal
confirmed that the emoji bytes are absent under `LC_CTYPE=C` and present under
`LC_CTYPE=C.utf8` while holding `LANG` constant. The final Compose exec now sets
only `LC_CTYPE=C.UTF-8` for the tmux client, preserving the broader language
locale; new servers inherit it too, and a second Docker reproduction confirmed
that a UTF-8 client renders emoji output from an existing C-locale server. The
new exact-argv test in
`t/91-cli-ticket-coverage.t` failed before the fix and passes after; all 280
focused assertions pass in Docker. The final 262-file full suite and coverage
gate also pass. Targeted security scans/tests pass; full ASVS, packaging, and
blank-install gates for combined Problem 48 work remain pending.

Problem 48 coverage and dependency follow-up (2026-10-12): the previous full
Docker run found the last uncovered PathRegistry condition combinations. The
coverage test now checks both a defined account environment value changing to
another defined value and metadata reads that return undefined content. Its
focused run passed 255 assertions; a fresh full run passed 263 files / 23,488
tests and all four library coverage metrics at 100.0% (46,834 detail rows, no
stale annotations). Dependency audit found HTTP::Tiny had no source usage but
was still listed by three runtime manifests. A red-first release metadata test
proved all three declarations were stale, so the prerequisite was removed;
focused release metadata tests pass. After the dependency edit, the full Docker
coverage run was repeated and passed 263 files / 23,488 tests at 100.0%
statement, branch, condition, and subroutine coverage (46,834 detail rows; no
stale annotations). The 50x-per-subroutine objective remains unmet.

Fresh Docker evidence separates prompt bytes from tmux locale handling:
`d2 ps1 --no-indicators` emitted identical UTF-8 ticket (`f0 9f 8e ab`) and
branch (`f0 9f 8c bf`) bytes under both `LC_CTYPE=C` and `C.UTF-8`. A tmux 3.6
server/client matrix returned no emoji bytes when both used `C`; bytes were
present whenever either the server or attached client used `C.UTF-8`. The
workspace handoff sets the client locale, including when attaching to an
already-running C-locale server, without rewriting its or the pane process's
environment. This rules out the prompt payload and Compose YAML as the source.

Follow-up verification held `d2 ps1` output constant as valid UTF-8 bytes and
started a tmux server under `LC_CTYPE=C`. The tmux stream omitted the emoji
bytes for a C-locale client but included them for an attached
`LC_CTYPE=C.UTF-8` client. This isolates the root cause to the workspace handoff
not setting the tmux client's character locale, not to indicator data or
Compose YAML. Updating a client's locale does not rewrite an existing server
or pane process environment; the status-line rendering behavior is governed by
the client locale that the handoff now sets.

End-to-end tmux replay (2026-10-13): a uniquely named, isolated Compose
workspace used the installed `d2 workspace p46-e2e -c` inside its container.
Attaching without `LC_CTYPE` reproduced the report exactly: the prompt marker
and status icons appeared as `____ __h __`. Reattaching to that same tmux
server with `-e LC_CTYPE=C.UTF-8` rendered `✅🐳 🚨h 🕒` in the prompt/status
stream. The source `ps1` renderer and ticket/branch UTF-8 bytes were unchanged;
the only difference was the Docker exec client's locale. The workspace and
tmux test projects were removed after the check. The checkout's outer
`workspace` wrapper could not be exercised directly in this tool environment:
its nested raw Compose materialization was rejected with `Run d2 docker
compose ... instead of raw docker command`; the equivalent `d2 docker compose`
startup, alias registration, and both inner-workspace client handoffs were
tested directly instead.

Scope refinement (2026-10-13): a red-first Docker regression showed that the
first fix also forced `LC_CTYPE` in the general `workspace_environment`
builder, affecting ordinary non-Docker workspaces. That assignment is removed;
the locale remains explicit only on the final Docker Compose exec, where the
attached client and a newly started tmux server inherit it. The focused test
failed before and passed after this refinement (280 assertions). Repeating the
actual in-container `d2 workspace p46-final -c` attachment rendered
`✅🐳 🚨h 🕒` in the live tmux prompt/status; the uniquely named Compose
workspace was then stopped and removed.

Direct attached-client verification (2026-10-13): inside the running workspace
container, a uniquely named tmux server was created and attached through a
PTY. With `LC_CTYPE=C`, the client displayed the status icons as underscores;
with `LC_CTYPE=C.UTF-8`, it displayed the emoji. The isolated tmux server was
removed, and all pre-existing workspace sessions were unchanged. Docker
`t/91-cli-ticket-coverage.t` passed all 280 tests.

Problem 48 verification refresh (2026-10-13)

The cold-start Docker benchmark (two warm-ups and ten measured runs per
command) reported medians of 33.622 ms for `version`, 77.291 ms for
`paths -o json`, and 82.907 ms for `ps1 --no-indicators`. Compared with the
recorded v5.88 medians of 56.7, 93.3, and 109.5 ms, the current runs are about
1.69x, 1.21x, and 1.32x faster; the 50x objective remains unmet.

The full Docker suite passed 263 files / 23,556 tests with `init: true` and DAC
capabilities dropped. A preceding run without the init reaper exposed
descendant-reaping test failures, and `t/21-refactor-coverage.t` caught the
workspace locale leaking into the legacy ticket environment. The code now
scopes `LC_CTYPE=C.UTF-8` to the workspace handoff; seven focused regression
files passed 1,479 tests, and `t/91-cli-ticket-coverage.t` passes 280 tests.
The clean repeat of `script/coverage-gate` began with a clean database deletion
and passed 263 files / 23,494 tests and 100.0% statement, branch, condition,
and subroutine coverage across `lib/` (46,837 detail rows, no stale
uncoverable annotations). Seven environment or tooling-dependent files skipped
with explicit reasons. This verification does
not close packaging, integration, security-review, release-metadata, or git
delivery gates; the measured 50x-per-subroutine requirement is still open.

Problem 48 PathRegistry hot-path refinement (2026-10-13)

The NYTProf `paths` trace recorded 74 `_same_or_descendant_path` calls and
2.03 ms exclusive time. A red-first Docker regression around
`_ancestor_runtime_layers` observed three stop-directory identity resolutions
for a three-level walk. Lazily caching the stop identity reduced that count to
one while preserving the zero-canonicalization exact-root fast path. The final
259-test PathRegistry suite passed in Docker. This is a
specific call-count reduction; no 50x or wall-clock gain is claimed. Distinct
symlink spellings continue through canonical resolution.

Full Docker coverage gate (2026-10-13; before the optional-FFI follow-up):
after the PathRegistry test edits,
script/coverage-gate ran inside the isolated Docker bench with its coverage
database under the container's /tmp, leaving repository coverage databases
untouched. All 263 test files and 23,498 tests passed in 1,172 seconds.
Statement, branch, condition, and subroutine coverage each measured 100.0%
across lib/ (46,844 detail rows; no stale uncoverable annotations). Seven
environment- or tool-dependent test files reported explicit skips. The
required focused web-security trio also passed in Docker (3 files / 459 tests).
These checks do not prove the 50x performance target, complete the ASVS V1–V14
review, or close the remaining packaging and delivery gates.

Optional FFI dependency/fallback correction (2026-10-13): a red-first
release-metadata test caught that the adapter's optional FFI dependency was a
hard Dist::Zilla prerequisite and missing FFI modules still threw despite the
documented Perl fallback. The FFI modules are now runtime recommendations in
dist.ini, cpanfile, and MakeMaker metadata. _load_backend returns native
acceleration as unavailable only when the requested FFI module itself is
absent; errors inside installed modules remain visible. Red-first Docker tests
failed on all three missing-module cases, then the focused metadata, adapter,
and Auth suite passed 3 files / 5,616 tests.

Fresh-interpreter regression (2026-10-13): the previous absence checks loaded
the adapter while FFI modules were installed, then hid their `@INC` entries.
That missed compile-time `use FFI::Platypus` and `use FFI::Platypus::Buffer`,
which made the supposedly optional dependencies mandatory at adapter import.
A red-first Docker test started a new Perl interpreter with the FFI library
path hidden and failed with `Can't locate FFI/Platypus.pm`; the adapter now
loads FFI modules only inside `_load_backend`, so it imports without optional
FFI and `native_available` selects the documented Perl fallback. A second
regression keeps initialization errors from installed FFI modules visible.
The focused adapter run passed 35 tests and reached 100.0% statement, branch,
condition, and subroutine coverage.

The subsequent full Docker gate passed 263 files / 23,518 tests and 100.0%
statement, branch, condition, and subroutine coverage across `lib/` (46,868
detail rows, no stale uncoverable annotations). Seven environment- or
tool-dependent files skipped with explicit reasons. This is coverage evidence,
not evidence that the 50x performance target is met. Release build, blank
container installation, Docker image build, final security review, and git
delivery remain open.

Problem 48 cold-start refinement (2026-10-13): red-first Docker regressions
found that bin/dashboard eagerly imported unused Config and hook-only
IO::Handle, IO::Select, IPC::Open3, and Symbol. The switchboard now
defers/removes them; the 1,029-test hook-heavy CLI smoke suite passes. In a
paired 25-run Docker benchmark, the current lazy path measured 31.520 ms
versus 33.104 ms with those five modules preloaded (about 1.05x). This does
not meet the 50x target.

Problem 48 path CLI refinement (2026-10-13): current-checkout NYTProf showed
`path project-root` importing Config and FileRegistry without using them. A
red-first subprocess test caught the eager loads; the path CLI now requires
those modules only for verbs that consume configured aliases. The lazy-load
and path CLI suites passed 55 tests in Docker. An alternating 25-run
fresh-process benchmark measured 30.372 ms for the lazy candidate and 36.041 ms
for the control with both modules preloaded (about 1.19x). This is a modest
command-specific result, not a 50x result for every subroutine.

Problem 48 path option-parser refinement (2026-10-13): a corrected
checkout-only NYTProf trace showed Getopt::Long initialized during
`path project-root`, although that verb does not parse options. A red-first
Docker probe caught it in `%INC`; the CLI now requires the parser only when an
option-bearing verb invokes the shared parser helper. The focused lazy-load
and path suites passed 57 tests. An alternating 25-run process benchmark
measured 28.863 ms for the candidate versus 32.937 ms with Getopt::Long
preloaded (about 1.14x). This gain is limited to that route; the 50x target
across every subroutine remains unmet.

Problem 48 full coverage recheck (2026-10-13): the canonical isolated Docker
coverage gate passed all 263 files / 23,532 tests after the path option-parser
change. Statement, branch, condition, and subroutine coverage each measured
100.0% across `lib/` (46,890 detail rows, no stale uncoverable annotations).
Environment/tool-dependent tests reported explicit skips. This is complete
coverage evidence for the current test suite, not evidence of 50x speedup for
every subroutine.

Problem 48 cumulative project-root benchmark (2026-10-13): a Docker ABBA
comparison ran 20 fresh processes per mode through the same registry and
project-root handler. The current mode deferred Getopt::Long and skipped the
unused Config/FileRegistry objects; the control reproduced that prior setup.
Captured outputs matched byte-for-byte (`/checkout`). Medians were 26.852 ms
current and 35.489 ms pre-change (about 1.32x). This verifies only this route;
the 50x-per-subroutine requirement remains open.

Problem 48 path-only Config view refinement (2026-10-13): prompt and path CLI
configuration lookups now use Config->for_path_config, avoiding FileRegistry
loading and construction where the operations only need PathRegistry. The
first full Docker coverage run exposed an outdated t/43-explicit-coverage-qa.t
test double, which stubbed Config->new but not for_path_config. After updating
the fixture, the canonical gate passed 263 files / 23,544 tests; statement,
branch, condition, and subroutine coverage each measured 100.0% across lib/
(46,954 detail rows, no stale uncoverable annotations). The temporary
container ran perldoc perlsec after the gate. Environment-bound tests emitted
documented skips. The optimization benchmark measured 64.636 ms with
FileRegistry unloaded versus 72.077 ms with its module force-loaded (about
1.12x; import-only control). This does not establish the 50x target. Integration,
packaging, image, final release, and delivery gates remain open.

Problem 48 paths helper startup refinement (2026-10-13): a red-first
subprocess test showed share/private-cli/paths importing FindBin even when the
public switchboard had passed DEVELOPER_DASHBOARD_REPO_LIB before exec. The
helper now uses that supplied repo library path and retains FindBin for
standalone fallback invocation. Docker checks confirm no warnings and
byte-identical JSON from both routes. An alternating 100-pair cold-helper
benchmark measured 26.963 ms for the FindBin fallback versus 24.782 ms with
the supplied library path (about 1.09x). This is not an end-to-end public
command benchmark and does not close the 50x objective.

Problem 48 final verification refresh (2026-10-13): after the helper change and
release-metadata wording correction, the canonical Docker gate passed 263 files
/ 23,551 tests. Statement, branch, condition, and subroutine coverage each
measured 100.0% across lib/ (46,954 detail rows; no stale uncoverable
annotations). perldoc perlsec was run after this final gate. Optional tooling,
browser-loopback, QEMU, and Docker-daemon checks emitted explicit skips. The
50x-per-subroutine goal, integration, packaging, image-build, and git-delivery
gates remain open.

Problem 48 prompt helper startup refinement (2026-10-13): NYTProf showed about
2.4 ms inclusive in FindBin initialization for ps1 --no-indicators. A red-first
Docker test caught the helper loading FindBin despite the public switchboard
already supplying DEVELOPER_DASHBOARD_REPO_LIB. The helper now uses that path
and retains FindBin for direct standalone invocation. Tests verify no warning
on either route and equal prompt output apart from the live timestamp. An
alternating 25-pair cold-helper benchmark measured 33.364 ms for the supplied
path versus 33.671 ms for fallback (about 1.01x). This small saving does not
materially advance the 50x-per-subroutine target; the canonical full gate must
be rerun after this change.

Problem 48 prompt option parser refinement (2026-10-13): NYTProf showed about
0.9 ms exclusive in Getopt::Long initialization, plus callbacks and import
work. A red-first Docker check showed ps1 loading it for the canonical flags
emitted by generated shell prompts. The helper now parses exact canonical
options locally and loads Getopt::Long only for unsupported or abbreviated
syntax. Tests cover value parsing, invalid numeric fallback, abbreviation
support, output parity, and warnings. An alternating 40-pair cold-process
control measured 29.338 ms without Getopt::Long initialization versus 33.174
ms with it preloaded (about 1.13x). This only quantifies module startup savings;
it does not meet the 50x-per-subroutine target. A 30-pair public-command
control also measured 65.535 ms without Getopt::Long preloaded versus 72.307 ms
with it (about 1.10x), confirming the savings survive switchboard dispatch.
Focused Docker lazy-loading and dispatcher tests passed 146 assertions; the
canonical full gate must be rerun after these changes.

Problem 48 installed-skill config scan refinement (2026-10-13): a preliminary
ps1 NYTProf trace in the disposable bench container (its image HOME, with no
host home mount; not a hermetic skill fixture) showed Config resolving 30
skills with one skill_layers scan per skill, producing 40 runtime_roots calls.
The synthetic benchmark used its own temporary home. A new PathRegistry
grouped inventory returns each selected root and all layers in one pass; Config
uses it for collector and API-key reads without changing layer precedence or
disable semantics. Red-first Docker
tests verify deepest-root selection, home-to-project ordering, disabled
masking, and that Config avoids per-skill skill_layers calls. A synthetic
50-skill/four-layer benchmark checked exact serialized parity and measured
4.431 ms on the former repeated-scan path versus 3.409 ms grouped (about
1.30x). This is not the full dashboard or the 50x target; full coverage and
security gates must be rerun.

Problem 48 nested skill-alias chain reuse (2026-10-13) — in progress

The corrected checkout NYTProf trace showed nested alias merging rediscovered
the same skill layer chain to determine each entry's writable config location.
PathRegistry now optionally returns that location with its nested-skill
inventory; Config reuses it. Red-first Docker coverage confirms the existing
git-managed ancestor/global fallback behavior and fails if alias merging
resolves the chain again. Additional cases reject malformed and mismatched
pre-resolved chains. Four focused suites passed 564 tests. A synthetic
50-skill/two-layer/two-depth benchmark preserved exact serialized output and
measured 4.216 ms on the repeated resolver path versus 2.347 ms with chain
reuse (about 1.80x). This is a scoped routine improvement only; Problem 48's
50x target and full SDLC gates remain open.
The post-change checkout NYTProf call trace shows read-side alias discovery
using the already-resolved chain helper rather than the public resolver.

Problem 48 nested inventory validation follow-up (2026-10-13) — in progress

The next checkout NYTProf profile attributed 12.1 ms inclusive / 3.60 ms
exclusive to nested_skill_entries, which revalidated chain prefixes repeatedly.
A red-first Docker test failed on those counts. Discovery now validates each
segment once and carries the nearest git-free ancestor/depth forward; the
strict public resolver retains independent validation. Three focused Docker
suites passed 301 tests. A 50-skill/two-depth alternating benchmark preserved
canonical JSON and improved 5.407 ms to 2.048 ms (about 2.64x). The fresh
checkout trace shows 6.89 ms inclusive / 1.06 ms exclusive in the routine.
This does not meet the 50x all-subroutine objective; full delivery gates remain.

Problem 48: reuse nested inventory across path and Folder aliases
(2026-10-13) — in progress

The paths profile showed Config and Folder.pm discovery each traversing nested
skills. A red-first Docker test observed two nested_skill_entries calls. The
command now passes one inventory to both; disabled status propagates to
descendants so disabled Folder providers remain excluded while Config keeps
its include-disabled behavior. Five Docker suites passed 354 tests. The
post-change checkout profile confirms one nested inventory call rather than
two. An alternating synthetic 50-skill/two-depth benchmark preserved canonical JSON
and measured 4.355 ms separate versus 3.317 ms shared (about 1.31x). This
scoped gain does not meet the all-subroutine 50x objective or close delivery.

Problem 48: share skill config inventory for path aliases
(2026-10-13) — in progress

A red-first Docker regression found Config::path_aliases loading the
installed-skill config inventory once through merged() and again for the
skill-qualified aliases. Config now builds it once and passes it to both
readers; malformed shared options and rows fail explicitly. Five focused
Docker suites pass 360 tests. A synthetic 50-skill/two-layer benchmark
preserved canonical JSON and measured 4.304 ms with two inventories versus
2.342 ms with one (about 1.84x). This is one routine's gain; the 50x target
and complete SDLC gates remain open.

Problem 48: normalize runtime-layer candidate paths once
(2026-10-13) — in progress

A fresh checkout profile showed runtime_layer_root_for canonicalizing one
candidate repeatedly while it checked multiple runtime roots. A red-first
Docker regression failed with three candidate identity resolutions; the
method now performs one and still resolves every root against the filesystem
before applying the same component-boundary comparison. Five focused Docker
suites pass 438 tests. A 100-pair five-root benchmark preserved the selected
root and measured 0.059 ms before versus 0.042 ms after (about 1.41x). This is
a scoped helper gain, not the 50x-per-subroutine target; full gates remain.

Problem 48: merge only the skill path-alias config domain
(2026-10-13) — in progress

Path-alias output consumes only each skill's path_aliases field. The reader
now merges that domain across grouped skill layers, instead of recursively
merging unrelated config fields and full _skill-name namespaces. A red-first
Docker assertion caught the old unrelated-field merge; seven focused suites
pass 676 tests with identical alias output. On a synthetic 50-skill/two-layer
fixture with 80 unrelated fields, 30 alternating pairs measured 22.262 ms
before versus 9.774 ms after (about 2.28x). This scoped result remains below
50x; the objective and remaining SDLC gates are open.

Problem 48 full Docker verification recheck (2026-10-13) — in progress

The first full Docker run omitted an init/reaper and failed five
process-descendant test files; those files passed 277/277 with Compose
init: true. Red-first assertions now cover malformed Config inventory inputs
and nested-skill write-location/disabled-state conditions. The final full
Compose-init gate passes 263 files / 23,604 tests and reports 100.0% statement,
branch, condition, and subroutine coverage across lib/ (47,304 detail rows,
no stale uncoverable annotations). Browser, Windows-QEMU, kwalitee,
CPAN-audit, and Docker-daemon-only checks have documented environment/tool
skips. Coverage is closed for this revision; Problem 48 remains open because
the 50x performance objective and remaining SDLC gates are not met.

Problem 48: request-scoped runtime-layer candidate discovery
(2026-10-13) — in progress

The fresh 50-skill NYTProf profile showed 15 calls to _ancestor_runtime_layers
from runtime_layer_root_for during one paths request. all_paths now shares the
candidate list only within its aggregate operation; no registry-wide memo is
retained. Red-first Docker coverage failed with 16 walks before the change and
now passes with at most two in the focused fixture. A later lookup sees a
project .d2 layer created after all_paths returned. A 15-lookup parity
benchmark measured 1.162 ms without the scope versus 0.305 ms with it (about
3.81x). The full 50-skill profile recorded five total ancestry walks after the
change, down from 16. This scoped gain does not meet the 50x-per-subroutine
target; Problem 48 remains in progress.

Problem 48: defer path table formatting on JSON output

Problem 49: Fix the Homebrew install regression test's hardcoded /tmp path
  GitHub issue 59 reproduced assertion 195 failing when TMPDIR pointed outside
  /tmp. install.sh already honours TMPDIR; only the test expectation was wrong.
  The test now isolates TMPDIR and derives both command paths with File::Spec.
(2026-10-13) — in progress

A red-first Docker regression found that the JSON-only paths helper eagerly
loaded Developer::Dashboard::CLI::TableHelpers. It now builds the PathRegistry
directly and requires the renderer only in human/table output branches.
Focused Docker tests verify no renderer import on JSON output, preserved table
headings, and byte-identical output on the standalone FindBin fallback. Thirty
alternating cold helper launches measured 29.030 ms lazy versus 29.210 ms with
TableHelpers forced eager (about 1.006x). This is negligible startup change,
recorded as import hygiene rather than a material gain. Problem 48's 50x
per-subroutine target remains unmet.

PBKDF2 native-path test coverage follow-up (2026-10-13)

Problem 48's PBKDF2 adapter was at 63.4% total coverage because this Docker
image lacks optional FFI packages, so native-success and native-error branches
were not executed. t/651-crypto-pbkdf2-ffi.t now supplies a deterministic fake
FFI boundary independent of installed modules or system libcrypto. It verifies
cached/lazy initialization, PBKDF2 digest parity, UTF-8 byte-boundary decline,
signed-int input limits, short-output rejection, missing/empty library
discovery, null digest, and native call failure. The Docker test passes 54
assertions; an isolated Devel::Cover run reports 100.0% statement, branch,
condition, and subroutine coverage for Crypto/PBKDF2.pm. The subsequent full
Docker gate passed 263 files / 23,272 tests and reports 100.0% statement,
branch, condition, and subroutine coverage across lib/ (47,329 detail rows; no
stale uncoverable annotations). perldoc perlsec completed successfully in the
benchmark container. Coverage is verified for this worktree; Problem 48's
50x-per-subroutine target remains open.

Problem 48 request-scoped installed-skill directory inventory (2026-10-14)

The 50-skill NYTProf trace found that one paths -o json invocation read the
same top-level skills directories twice: once for nested-skill discovery and
again for layered Config entries. A red-first Docker test observed two
skills_roots calls where a shared request snapshot should need one. The
PathRegistry now shares only sorted directory names for the lifetime of one
run_paths_command; disabled markers are still checked by each consumer, and
the scope is discarded before the next command. The test creates a new skill
after the scope and confirms the next inventory sees it. Four focused Docker
suites passed 5,860 tests. A two-layer 50-skill microbenchmark preserved
canonical output and measured 1.465 ms per paired inventory without sharing
versus 1.136 ms with sharing (about 1.29x). Matched NYTProf command time moved
from 15.9 ms to 14.9 ms under instrumentation. This modest scoped gain does
not meet the 50x-per-subroutine objective.

Problem 48 MCE config-workload benchmark (2026-10-14)

To test MCE against a candidate workload rather than synthetic sleeps, the
scoped Docker container read and decoded 50 on-disk JSON config fixtures, each
with 80 unrelated fields and one path alias, then compared canonical alias
output byte-for-byte. Serial median was 1.125 ms. Four-worker MCE took 4.944 ms
on its first map call and 1.440 ms median with workers already warm (0.78x
versus serial). This does not justify parallel config loading in the one-shot
CLI; MCE remains useful only where measured independent work exceeds worker
and data-transfer overhead. The full Problem 48 objective remains open.

Problem 48 MCE scaling at large config counts (2026-10-14)

An isolated Docker benchmark processed ordered, independent JSON configs with
10,000 and 20,000 fixtures (80 unrelated keys plus one alias each). Exact
serial/MCE result parity held. At 10,000 entries, four-worker warmed MCE was
290.222 ms versus 369.181 ms serial (1.27x). At 20,000, four workers measured
393.896/490.490 ms (1.25x) and eight workers 341.180/435.242 ms (1.28x).
First-map timing ranged from 0.98x to 1.63x serial. This is a 25-28% gain on
an unusually large synthetic config-read workload, not 50x; production config
loading remains serial. Problem 48 remains open.

Problem 48 SPVM HMAC boundary benchmark (2026-10-14)

The container SPVM SHA module produced matching SHA-256/HMAC bytes after its
SPVM string result was converted to binary. Over 30,000 chained HMAC-SHA256
iterations, the Digest::SHA XS loop measured 40.680 ms median; calling SPVM
from Perl and converting each result measured 182.891 ms (0.22x). A per-
iteration Perl/SPVM boundary is slower despite exact parity. This does not
test a PBKDF2 loop compiled wholly inside SPVM. A follow-up benchmark kept the
entire 210,000-round PBKDF2-HMAC-SHA256 loop inside an SPVM method. Five
alternating Docker runs produced exact digest parity among Perl, OpenSSL FFI,
and SPVM. Median timings were Perl 284.853 ms, SPVM 522.992 ms (0.54x Perl
speed), and OpenSSL FFI 31.834 ms (8.95x Perl speed); SPVM load/compile took
48.842 ms and was excluded from routine medians. The full-loop SPVM approach
is slower and is rejected for production. Reproduction is provided by
script/problem48-spvm-benchmark; t/655-spvm-hmac-loop.t checks its fixture's
digest contracts. Problem 48 remains open.

Problem 48 verification refresh (2026-10-14)

After adding the compiled-SPVM benchmark fixture, the complete scoped Docker
coverage gate passed 264 files / 23,284 tests and reported 100.0% statement,
branch, condition, and subroutine coverage across lib/ (47,343 detail rows,
no stale uncoverable annotations). Nine optional/environment-dependent tests
reported explicit skips; this includes the optional SPVM test in the full-gate
environment, while the targeted SPVM test passed all five assertions in the
benchmark container where its dependencies are installed. The required
post-gate perldoc perlsec check succeeded. These checks do not satisfy the
50x-per-subroutine target; the full security, integration, dependency, release,
artifact, and delivery gates remain pending.

Problem 48 aggregate-scoped path identity reuse (2026-10-14)

The current NYTProf map showed path normalization repeated during the aggregate
all_paths operation. A red-first Docker regression failed before the change:
the same input was normalized twice in an explicit scope, and all_paths did
not open an identity scope. PathRegistry now memoizes exact input spellings
only for the duration of all_paths; after return, later calls resolve the
filesystem again. The focused Docker suite passes 281 assertions. A 51-pair
alternating benchmark preserved byte-identical 1,171-byte JSON and measured
0.576 ms uncached versus 0.424 ms scoped (1.36x for all_paths). The absolute
gain is about 0.15 ms in a sub-millisecond routine and does not establish the
50x target. Full Docker coverage and post-gate perlsec must be rerun after this
source change; Problem 48 remains open.

Problem 48 post-change coverage/security verification (2026-10-14)

The full scoped Docker gate was rerun after the PathRegistry change and passed
264 test files / 23,292 tests. Statement, branch, condition, and subroutine
coverage are all 100.0% across lib/ (47,360 detail rows, no stale uncoverable
annotations). Nine optional/environment-dependent tests skipped with explicit
reasons. The SPVM test passed separately in its dependency-ready container.
After the gate, perldoc perlsec succeeded (29,990 bytes). The performance
objective and remaining integration/release/delivery gates remain open.

Problem 48 nested-skill marker check (2026-10-14)

nested_skill_entries(include_disabled => 1) redundantly checked the top-level
skill .disabled marker while listing roots and again while building the
traversal queue. A red-first Docker regression caught the duplicate; each root
is now checked once and empty inputs remain safe. Exact output parity held in
51 paired runs over 50 skills (0.599 ms to 0.496 ms, 1.21x) and 31 paired runs
over 250 skills (3.089 ms to 2.585 ms, 1.19x). The current full Docker gate
passed 264 files / 23,296 tests with 100.0% statement, branch, condition, and
subroutine coverage across lib/ (47,375 detail rows); nine
optional/environment-dependent tests skipped explicitly. Post-gate perldoc
perlsec succeeded. The 50x-per-subroutine Problem 48 goal remains open.

Problem 48 cold-process performance floor evidence (2026-10-14)

An alternating Docker control launched 25 fresh dashboard version processes
and 25 fresh bare Perl processes with the same interpreter. The medians were
32.424 ms and 1.263 ms respectively. Against the recorded v5.88 dashboard
median of 56.7 ms, a 50x result requires 1.134 ms total, below bare interpreter
startup. Thus a cold Perl process cannot meet the command-level target on this
runtime. A resident/native entry path is necessary, but its complete mutation
invalidation and output parity remain unproven. This does not satisfy the
separate per-subroutine target.

Problem 48 refreshed NYTProf path profile (2026-10-14)

A Docker NYTProf run exercised current paths -o json with 50 synthetic skills
and returned 2,969 bytes. Exclusive profiler line totals showed 3.341 ms at
the PathIdentity import boundary, 2.937 ms at Config's JSON import boundary,
2.032 ms for 50 config-path constructions, 0.520 ms for 50 disabled-marker
checks, and 0.285 ms across 100 JSON decode lines. These are instrumented
times, not unprofiled wall-clock gains; import lines include one-time setup.
Marker checking is no longer the next profile target. The 15-pair MCE
benchmark preserved ordered output at 50, 250, 1,000, and 5,000 configs. Four
workers with a fresh pool per size produced cold ratios versus serial of 0.21x,
0.90x, 0.97x, and 0.89x; warmed ratios were 0.92x, 1.18x, 1.13x, and 1.18x.
MCE module import was excluded, so cold MCE cost is understated. Cold one-shot
MCE was slower at all tested sizes; these results do not justify production
integration.

Problem 48 full gate after MCE benchmark test (2026-10-14)

The full Docker test gate passed 265 files and 23,316 tests. Statement, branch,
condition, and subroutine coverage each measured 100.0% across lib/ (47,375
detail rows, without stale uncoverable annotations). Nine optional or
environment-dependent test files reported explicit skips; three MCE-specific
assertions also skipped because MCE is optional and absent from the standard
gate image. The focused MCE-enabled Docker test passed all 11 assertions.
Post-gate perldoc perlsec succeeded (29,990 bytes). These results validate the
current test gate, not the 50x performance objective or remaining delivery
gates.

Problem 48 Inline::C/XS and Memoize benchmark gate (2026-10-14)

After adding the reproducible Inline::C/XS and operation-scoped Memoize
benchmarks and their regression tests, the full isolated Docker coverage gate
passed 267 files / 23,366 tests. Statement, branch, condition, and subroutine
coverage were 100.0% across lib/ (47,381 detail rows, with no stale
uncoverable annotations). Optional/environment-dependent tests reported
explicit skips. Both new benchmark test files passed in that run, the new
scripts and test files passed Perl syntax and POD checks, and the required
post-gate perldoc perlsec check passed. This verifies test coverage and
security-document checks for the iteration; it does not meet the 50x
all-subroutine performance target.

Problem 48 resident-cache freshness precondition (2026-10-14)

The focused Docker run of t/97-pathregistry-coverage.t passed all 293
assertions. It confirms that one PathRegistry instance sees a skill added
after a request-local inventory scope, a newly created disable marker, removal
of that marker, and deletion of the skill directory on later lookups. The same
Config and PathRegistry objects also see replaced skill config bytes and a new
skill alias and a retargeted runtime symlink. This does not establish
persistent-cache freshness or the 50x target.

The focused Docker run of t/90-cli-paths-coverage.t passed 33 top-level tests,
including a provider whose Folder.pm method reads a sidecar file outside the
skill tree. Repeating alias enumeration through the same paths object after
changing the file returned the updated target. A watcher limited to
skill/config roots cannot detect arbitrary user-module dependencies, so a
resident optimization must not memoize user Folder.pm method results.

Problem 48 tiny FFI boundary probe (2026-10-14)

A three-second Docker microbenchmark compared PathRegistry::home with
FFI-bound libc getenv("HOME"). Both returned the same string in the measured
environment; the Perl accessor ran at 12.43 million calls/second and the FFI
call at 5.58 million calls/second (about 2.23x slower through FFI). They are
not semantically identical under environment mutation, so this is boundary-
cost evidence only, not a function speed comparison.

Problem 48 stale runtime-layer memoization (2026-10-14)

The red-first Docker regression failed because one PathRegistry instance did
not see a project .developer-dashboard directory created after its first
runtime_layers lookup. It also confirmed stale inclusion after removal. The
implementation now refreshes unscoped discovery and caches only inside the
all_paths or path CLI operation scope. The focused PathRegistry Docker suite
passes all 298 assertions, including same-object create/remove and bounded
scoped reuse. This is a freshness fix, not evidence of the 50x target.
The subsequent full Docker suite passed 265 files / 23,392 tests. A fresh
script/coverage-gate run passed 265 files / 23,330 tests and reported 100.0%
statement, branch, condition, and subroutine coverage across lib/ (47,381
detail rows). Required web-update, static-file, and SSL security tests also
passed. This validates the current worktree but does not satisfy the separate
50x-per-subroutine performance objective.

Problem 48 matched per-routine profile (2026-10-14)

The same paths -o json helper was profiled in Docker from the v5.88 source and
current checkout with identical HOME/cwd inputs. Byte comparison of JSON
outputs passed. NYTProf reported 59.4 ms for v5.88 and 47.9 ms currently
(1.24x at handler level). PathRegistry::_path_identity exclusive total
changed from 2.37 ms over 133 calls to 90 microseconds over 103 calls; per
call, that is about 20x, still below the 50x target. This covers only routines
reached by this command and cannot establish the all-subroutine objective.

Problem 48 Inline::C/XS PBKDF2 comparison (2026-10-14)

A new reproducible benchmark and red-first regression compare the Perl
PBKDF2-HMAC-SHA256 reference, the existing FFI adapter, and an Inline::C
generated XS wrapper that invokes the same OpenSSL API. Docker tests pass nine
assertions and all implementations return identical digests. At the production
210,000-round work factor, five runs measured 287.413 ms Perl, 31.073 ms FFI
(9.25x), and 31.379 ms Inline::C/XS (9.16x); Inline compilation added 610.263
ms outside the routine medians. Inline adds no meaningful improvement beyond
FFI and remains benchmark-only. Problem 48 remains open.

Problem 48 request-scoped Memoize comparison (2026-10-14)

A new nine-assertion Docker regression verifies exact results and symlink
refresh for uncached path identity, an operation-local hash, and Memoize
flushed at each operation boundary. Across 15 runs of 1,000 unique paths
repeated twice, medians were 7.705 ms uncached, 4.246 ms with the local hash
(1.81x), and 5.006 ms with Memoize (1.54x). Memoize is slower than the existing
scoped hash and is not adopted. The 50x-all-subroutine target remains unmet.

Problem 48 resident path-context parity and mutation probe (2026-10-14)

A new Docker prototype reuses PathRegistry/Config objects across path requests,
but refreshes current directory, object memo state, configured aliases, skill
inventory, config contents, and dynamic Folder.pm providers each time. Ten
focused assertions passed exact output parity against a fresh helper and
verified config replacement, changing a Folder.pm environment dependency, and
skill addition/removal between requests. Nine alternating runs over 50 skills
measured 29.336 ms for the fresh helper and 6.719 ms for the resident handler
(4.37x). This does not establish a 50x route and does not justify a production
daemon. Problem 48 remains open.

Problem 48 full Docker coverage/security gate after resident-context probe (2026-10-14)

The isolated full Docker suite passed 268 files and 23,385 tests. The canonical
coverage gate reported 100.0% statement, branch, condition, and subroutine
coverage across lib/ (47,381 detail rows; no stale uncoverable annotations).
Environment-dependent checks explicitly skipped, including missing
cpan-audit, unavailable browser loopback, an unreachable Docker daemon,
optional SPVM/kwalitee tooling, and operator-local .claude specs. The
benchmark-only SPVM test also skipped in the standard image. Post-gate
perldoc -t perlsec passed, git diff --check was clean, and the scoped
Problem 48 Compose project had no remaining containers. These gates do not
establish the 50x all-subroutine performance target or finish Problem 48.

Problem 48 built-in Perl object/signature probe (2026-10-14)

In the isolated Perl 5.40.1 Docker runtime, a seven-run alternating probe
compared two million blessed-hash method calls with built-in
class/field/method calls, and ordinary subs with signature subs. Results
matched exactly. Medians: conventional method 179.731 ms; built-in object
230.533 ms (0.78x relative speed); ordinary sub 142.308 ms; signature sub
196.421 ms (0.72x). Perl emitted experimental warnings for class, field, and
method. The feature path is not adopted because it is slower and violates the
warning-free gate; warnings were not suppressed. This synthetic probe does
not establish project-wide or 50x gains. Problem 48 remains open.

Problem 48 validated working-directory lookup recheck (2026-10-14)

A red-first Docker assertion verifies the ordinary absolute PWD match uses
the direct getcwd result while distinct logical symlink paths still require
device/inode validation. The focused suite passes 17 assertions, including the
empty-getcwd fallback. Seven alternating 100,000-call runs measured 262.482 ms
for the previous current-branch implementation, 52.454 ms after the change,
and 41.729 ms for a same-subroutine v5.88 eval { getcwd() } wrapper. This is
5.00x faster than the previous current implementation but only 0.80x baseline
speed; it is not claimed as a v5.88 performance win. Problem 48 remains open.

Problem 48 full Docker coverage/security gate after working-directory recheck (2026-10-14)

The isolated suite passed 268 files / 23,388 tests. The canonical gate reports
100.0% statement, branch, condition, and subroutine coverage across lib/
(47,390 detail rows, no stale uncoverable annotations). Environment-dependent
checks explicitly skipped with reasons. Post-gate perldoc -t perlsec passed,
as did POD syntax and git diff --check. The 50x all-subroutine objective and
remaining delivery gates are still open.

Problem 48 resident-context scaling check (2026-10-14)

The resident prototype was measured with 250, 1,000, and 5,000 synthetic
skills, three alternating runs per size. Exact response parity and refresh
checks for config replacement, Folder.pm environment changes, skill creation,
and deletion passed. Fresh/resident medians were 38.091/13.887 ms (2.74x),
75.883/50.994 ms (1.49x), and 236.497/226.402 ms (1.04x). This confirms that
context reuse alone loses its benefit as filesystem discovery grows. The next
experiment will measure safely invalidated inventory/config caching while
still executing user Folder.pm code for every request. Problem 48 remains
open.

Problem 48 application-level MCE alias benchmark (2026-10-14)

A nine-assertion Docker regression measures the real Config::path_aliases API:
skill discovery and layered alias reads, with production merge, qualification,
and expansion retained. Canonical serial output matched all MCE results. At
four workers, serial/MCE warmed medians were 48.332/52.719 ms for 1,000 skills
(0.92x), 300.048/350.766 ms for 5,000 (0.86x), and 619.155/587.044 ms for
10,000 (1.05x). At eight workers, results were 284.234/302.021 ms for 5,000
(0.94x) and 625.528/662.299 ms for 10,000 (0.94x). The isolated 10k/four-
worker gain was not stable across worker counts; MCE will not be integrated
into production config reads based on this evidence. Problem 48's 50x target
remains open.

A longer nine-run, four-worker recheck at 10,000 skills preserved exact alias
output and measured 671.440 ms serial versus 635.286 ms warmed MCE (1.06x).
That synthetic gain does not justify worker lifecycle complexity in normal
configuration reads and does not materially advance the 50x target.

Problem 48 Docker coverage/security gate after application-level MCE probe
(2026-10-14)

The isolated coverage gate passed 269 files and 23,406 tests. Statement,
branch, condition, and subroutine coverage were each 100.0% across lib/
(47,390 detail rows; no stale uncoverable annotations). The standard image
explicitly skipped optional/environment-dependent cpan-audit, operator-local
.claude specs, browser loopback, post-build Docker guard, SPVM, Test::Pod, and
release kwalitee checks. The MCE-enabled focused test passed exact alias
parity separately. Post-gate perldoc -t perlsec, benchmark script syntax/POD
checks, and git diff --check passed. This closes only the current test and
coverage gate; the 50x target and release/integration gates remain open.

Problem 48 operation-scoped ancestor discovery (2026-10-14)

A red-first Docker test found all_paths walked ancestor runtime layers twice:
once during runtime_layers and again during protected-path checks. The existing
operation-local scope now shares the discovered list between both call paths.
A focused assertion verifies exactly one walk; unscoped lookups remain fresh.
The focused PathRegistry and I/O suites passed 325 assertions, and 51
alternating all_paths runs kept exact JSON parity. This removes duplicate work
but is not a 50x speedup claim. Problem 48 remains open.

Problem 48 full Docker verification after ancestor-walk change (2026-10-14)

The first full gate caught a README/POD generation mismatch introduced while
documenting this change. The canonical README was synchronized and the
targeted release-metadata test passed. The rerun passed 269 files and 23,406
tests. All four coverage dimensions are 100.0% across lib/ (47,408 detail
rows, no stale uncoverable annotations). Optional/environment-dependent checks
reported explicit skips. Post-gate perlsec, Perl syntax/POD checks, and
git diff --check passed. This verifies the current code/docs gate, not the
50x-per-subroutine target or remaining packaging/release gates.

Problem 48 indicator-list optimization (2026-10-14)

NYTProf on the actual /system/status route attributed 5.534 ms inclusive to
IndicatorStore::list_indicators. It found that the method resolved indicator
roots for enumeration and then repeated the same resolution for each record. A
red-first Docker test reproduced five root scans for four records; listing now
resolves the roots once and passes them to a private reader. Root precedence,
fresh file reads, and sorted output remain unchanged. The focused suites passed
242 assertions and the reproducible benchmark test passed 12. Nine paired
Docker runs with exact JSON parity measured 8.06x for one layer and 12.15x for
two layers. These targeted gains do not meet the 50x all-subroutine objective.
The full Docker gate then passed 270 test files / 23,433 tests with 100.0%
statement, branch, condition, and subroutine coverage across lib/ (47,422
detail rows); post-gate perlsec passed. Optional/environment-dependent tests
reported explicit skips. Problem 48 remains in progress.

A follow-up tested direct reads from the directory currently being enumerated
to avoid probing deeper roots. The red-first read-count regression failed at
three versus two reads, then passed with the candidate. A three-way benchmark
isolated this from the prior root-discovery gain: over 15 paired Docker runs at
50 / 250 / 1,000 records, direct reads improved the intermediate implementation
by only 1.00x / 1.04x / 1.02x with exact JSON parity. Since the difference is
within noise, the production experiment was reverted; no unsupported speedup
claim is retained. Problem 48 remains in progress.

Follow-up: Problem 48 indicator inventory reuse (2026-10-14)

A Docker NYTProf run of t/03-web-app.t found two full indicator listings per
configured-collector status payload: collector reconciliation listed once for
orphan cleanup, then payload construction listed again. The profile recorded 88
list_indicators calls across 44 payload/summary builds. A red-first route test
required one listing and byte-identical response JSON. The sync method now
returns its final inventory for the response builder and refreshes it after
orphan deletion; its default written-record return remains unchanged. The
post-change profile recorded 46 listings across those same 44 builds and
reduced instrumented _page_status_payload time from 3.09 ms to 2.53 ms per
call (1.22x). The focused web, IndicatorStore, and collector suites passed 580
assertions. The overall 50x target remains unmet.

The canonical isolated Docker coverage gate then passed all 270 test files /
23,445 tests. Statement, branch, condition, and subroutine coverage were each
100.0% across lib/ (47,452 detail rows), with no stale uncoverable
annotations. Optional environment/tool-dependent checks emitted explicit
skips. Post-gate perldoc -t perlsec, syntax and POD checks for both changed
modules, and git diff --check passed. This closes verification for this
iteration, not Problem 48's 50x-per-subroutine performance objective.

Follow-up: Problem 48 legacy bookmark parser performance (2026-10-14)

The web-test NYTProf profile attributed 482 ms exclusive to
PageDocument::_parse_legacy_sections over 148 calls. A red-first Docker test
confirmed each CODE1000 section scanned all 1,008 entries in @LEGACY_KEYS.
The optimized parser uses a fixed-name lookup plus the existing digit-based
CODE rule, preserving the observed acceptance of CODE1001. Exact-output,
21-run Docker comparisons measured 122.13x for 10 CODE sections and 127.47x
for 1,001; the matched web-test profile then measured 3.59 ms exclusive across
the same 148 calls. This is a large parser-specific gain, not proof of the
50x-every-subroutine objective. The full Docker gate passed 270 test files /
23,448 tests with 100.0% statement, branch, condition, and subroutine coverage
across lib/ (47,455 detail rows) and no stale uncoverable annotations.
Optional tooling/environment checks emitted explicit skips. Post-gate
perldoc -t perlsec, syntax and POD checks, release-metadata tests, and
git diff --check passed.

Follow-up: Problem 48 home-root identity reuse (2026-10-14)

In the same Docker NYTProf web-test workload, PathRegistry::_ancestor_runtime_layers
ran 3,373 times and took 72.2 ms exclusive. A red-first Docker test found the
selected home runtime path was canonicalized twice while building the exclusion
set because it also appeared in the enumerated home-layer list. The method now
deduplicates identical path spellings before identity resolution; canonical and
.d2 sibling paths are still independently checked. The same profiled workload
retained 3,373 calls and fell to 56.4 ms exclusive (1.28x). Focused PathRegistry
and web tests passed 631 assertions. The full Docker gate then passed 270 test
files / 23,450 tests, with 100.0% statement, branch, condition, and subroutine
coverage across lib/ (47,461 detail rows) and no stale uncoverable annotations.
Optional environment checks emitted explicit skips. Post-gate perldoc -t
perlsec, syntax/POD, release-metadata, and diff checks passed. The 50x target
for every Dashboard subroutine remains unmet.

Follow-up: Problem 48 verified-PWD matched-path recheck (2026-10-14)

An alternating 21-pair Docker microbenchmark matched the prior shell-backed
Cwd::cwd call path against the direct-getcwd helper: 1.7025 ms/call versus
0.0009 ms/call (1,891.6x), with equal physical-path output. The common path
calls native getcwd once and returns immediately for matching PWD; distinct
absolute PWD values are validated by device/inode to preserve logical symlinks.
This gain is only against shell-backed Cwd::cwd, not native Cwd::getcwd. The
full Docker gate exposed one failure because the experimental stat-first
variant contradicted the pre-existing one-getcwd test; production code and its
new red-first test now agree with that contract. Focused PathIdentity,
PathRegistry, and web suites then passed 689 tests. The corrected full gate
passed 270 files / 23,458 tests with 100.0% statement, branch, condition, and
subroutine coverage across lib/ (47,464 rows, no stale uncoverables); optional
tools/environment checks emitted explicit skips. Post-gate perlsec rendered
successfully. Problem 48 remains in progress.

Follow-up: Problem 48 matched v5.88 static-render comparison (2026-10-14)

The v5.88 source at commit a8d1fa2a4064640baba7a8ed7339462d9d3c3060 and the
current tree were measured in the isolated Docker service with the same static
render benchmark. Across 21 alternating samples of 5,000 renders,
PageRuntime::_render_templates measured 0.13501 ms per call at v5.88 and
0.00079 ms on the current tree (about 171x), with exact UTF-8 output parity.
This establishes a speedup for one routine and workload only, not for the
whole dashboard or every subroutine. Problem 48 remains in progress.

Follow-up: Problem 48 Dancer request scopes and state-directory chmod (2026-10-14)

NYTProf on the real PSGI /system/status route showed that direct Dancer backend
methods bypassed Web::App::handle(), repeating runtime-layer discovery across
authorization and route dispatch. A red-first Docker test observed five
ancestry scans across two reads. The adapter now shares request-local
runtime-layer and installed-skill inventories across direct handlers and drops
them after each request. Eleven alternating Docker pairs of 100 requests
preserved exact response bodies and measured 0.5984 ms/request without the scope
versus 0.5696 ms/request with it (1.05x).

The steady-state profile also recorded 1,300 PathRegistry chmod calls across
100 requests. _ensure_state_dir now skips chmod only when the existing mode,
including special permission bits, is exactly 0700; other modes keep the
visible repair/error path. Red-first I/O tests cover both cases. A 21-run Docker
microbenchmark measured 0.002329 ms per call at v5.88 versus 0.001290 ms
currently (1.80x), while profile chmod calls fell to 100 per 100 requests.
Focused Docker suites passed 506 assertions.

The first full coverage attempt is invalid because source and test files changed
while it ran; the gate correctly refused to report coverage for that mixed
tree. One operator-local t/158 spec also failed because project selector
tira-ddd is unavailable in the container. A fresh run on a frozen tree is
required. No full-coverage percentage or 50x all-subroutine result is claimed.

Follow-up: Problem 48 coverage closure (2026-10-14)

The fresh Docker run completed 272 files / 23,601 tests but initially exposed
two uncovered defensive paths: the failed stat guard in PathRegistry state
directory hardening and the non-hash app-reference guard in Dancer request
scoping. Deterministic tests now exercise both; the focused tests passed 157
assertions. A subsequent clean full coverage gate passed 272 files / 23,603
tests with 100.0% statement, branch, condition, and subroutine coverage across
lib/ (47,773 detail rows, no stale uncoverables). Post-gate perlsec, syntax,
POD, and diff checks passed. Problem 48 remains in progress because the
50x-per-subroutine target and remaining release gates are not complete.

Follow-up: Problem 48 request-scoped path identity reuse (2026-10-14)

A direct warmed PSGI profile found repeated canonical path resolution during
/system/status requests. Across 100 baseline requests, there were 1,300 calls
to PathIdentity::_path_identity over five repeated exact path spellings. A
red-first test confirmed identical spellings were resolved twice in one
request; Dancer's request scope now caches these identities only until that
request returns. A later request resolves them afresh. The focused Docker test
passes 132 assertions. Across 100 byte-identical 558-byte responses, calls
fell from 1,300 to 600. Eleven alternating Docker pairs measured 0.478
ms/request without the request scope and 0.463 ms/request with it (1.03x).
The 50x-per-subroutine goal remains open.

Follow-up: Problem 48 Dancer request argument accessor reuse (2026-10-14)

A red-first Docker test counted 13 active-request acquisitions, four PSGI
environment reads, and eight header reads in one request normalization. The
adapter now captures the request and environment once and reuses the header
map, preserving Host and remote-address fallbacks. The focused Docker test
checks one request acquisition, one environment read, seven required header
reads, and one body read. A canonical-JSON parity probe compared 200,000
legacy-equivalent and optimized calls per arm across five alternating rounds:
5.568 versus 3.665 microseconds per call (1.52x). This is not an end-to-end
route result and does not meet the 50x-per-subroutine objective.

Follow-up: Problem 48 PSGI header accessor reduction (2026-10-14)

Inspection and a Docker parity probe verified that Plack's seven selected
headers map exactly to fixed standard PSGI HTTP_* environment keys. A red-first
test failed while the adapter still invoked request->header; the optimized
path reads those values from the single captured environment instead. Tests
cover exact header parity, invalid environment errors, and the no-argument
helper path. Five alternating Docker pairs of 200,000 calls each measured
5.167 versus 2.578 microseconds per call (2.00x), with canonical JSON payload
parity. This remains a helper-only gain, not a 50x-every-subroutine result.

Follow-up: Problem 48 MCE indicator-list experiment (2026-10-14)

An isolated Docker probe compared the current full IndicatorStore list path to
an ordered MCE map using identical names, roots, reader, and priority sort.
Canonical JSON output matched. Nine alternating warm pairs measured
serial/MCE medians of 1.546/6.940 ms for 50 indicators, 20.020/22.656 ms for
1,000, and 97.739/78.368 ms for 5,000. A separate fresh-process 5,000-record
run measured the first MCE call at 63.449 ms versus 93.633 ms serial; warm
MCE was 89.454 ms. MCE regresses normal list sizes and its large-inventory
gain is inconsistent and modest, so it was not adopted. The full 50x target
remains open.

Follow-up: Problem 48 indicator sorting fast path (2026-10-14)

A Docker sort microbenchmark found repeated method dispatch and hash lookups
inside the indicator comparator. A red-first regression verified that large
lists used the legacy comparator. Lists of at least 32 records now precompute
the ordering fields, sort decorated tuples, and return the original records;
small lists retain the shared comparator. On identical ordered input, 21
30 alternating pairs at 5,000 records preserved exact order and measured
16.240 ms legacy versus 10.763 ms decorated sorting (1.51x). Full two-layer
list measurements were 1.04x at 50, 1.14x at 1,000, and 1.06x at 5,000, with
exact canonical JSON parity. The
larger isolated gain does not transfer to the full file-backed workload.
Two full Docker gates reported 99.9% condition coverage because Perl's sort
comparison schedule did not reliably hit every short-circuit pair. The tuple
comparison is now a small private function with direct deterministic tests
for priority, two-collector ordering, equal-order names, and mixed/unmanaged
name fallbacks. The final clean Docker gate passed 272 files / 23,628 tests
and measured 100.0% statement, branch, condition, and subroutine coverage
across lib/ (47,832 detail rows, no stale uncoverable annotations). Post-gate
perlsec passed, as did the required web-security tests (459 assertions) and
changed-file syntax/POD checks. Problem 48 remains open because these scoped
gains do not meet the all-subroutine 50x target.

Follow-up: Problem 48 File::Spec path assembly candidate (2026-10-14)

The matched paths profile highlighted 50 File::Spec->catfile calls in skill
config lookup. A Docker microbenchmark appeared promising, so the candidate
was measured through the full _skill_config_hash routine using 50 on-disk JSON
files, including existence checks, reads, decoding, and merging. Across 21
alternating pairs of 20 calls, direct joining measured 11.676 ms versus
12.367 ms with catfile (1.06x). The full-path gain does not justify weakening
cross-platform path handling. The candidate was rejected; production code was
not changed. Problem 48 remains open.

Follow-up: Problem 48 current-directory identity candidate (2026-10-14)

The current current_working_directory helper had measured 0.80x the v5.88
helper in an earlier matched benchmark. A Docker candidate validated absolute
PWD against the current directory using device/inode stats before falling
back to getcwd. Across 25 alternating pairs of 100,000 same-directory calls,
the candidate took 248.306 ms versus 56.970 ms for the current helper (0.23x).
Extra stats made it slower, so no code changed. Problem 48 remains open.

Follow-up: Problem 48 current web-test profile (2026-10-14)

A new NYTProf profile of t/03-web-app.t passed 351 tests. The PBKDF2 fallback
and PathRegistry layer discovery ranked among the largest project costs;
optional FFI modules were absent from the default bench @INC, so the PBKDF2
timing is not the native path. Direct instrumentation corrected a misleading
IO::Select::can_read stack count to 15 actual calls. A single-regex page-ID
normalization candidate had exact sample parity but measured 0.93x over 25
alternating Docker pairs of 100,000 calls. It was rejected; no production
code changed. Problem 48 remains open.

Follow-up: Problem 48 isolated paths profile and MCE scale check (2026-10-14)

NYTProf around the actual paths -o json operation with 250 installed skill
configs produced valid JSON (13,715 bytes, 264 aliases). Config::path_aliases
measured 14.7 ms inclusive under profiling; 250 _skill_config_hash calls took
2.8 ms exclusive total, disable checks 1.56 ms, and nested skill discovery
11.5 ms inclusive. At 250 skills, a production-API MCE comparison preserved
exact aliases but regressed: serial 14.400 ms, four-worker MCE 20.677 ms warm
and 19.209 ms cold (0.70x and 0.75x relative speed). MCE was not adopted.
Problem 48 remains open.

Follow-up: Problem 48 lookup-table HTML escape candidate (2026-10-14)

A red-first Docker regression required exact parity from a byte-indexed C
lookup-table candidate. The compiled staged benchmark passed byte, UTF-8,
NUL, undef, empty, and full-highlighter parity. Across 21 rotated samples, the
lookup table was slower than the production XS byte loop at 1 MiB: 11.04x vs
13.16x over Perl for escaping, and 8.18x vs 9.26x for full highlighting. It
was rejected and removed from production; the benchmark keeps the candidate,
and the XS suite retains a 1 MiB parity case. Problem 48 remains open.

Follow-up: Problem 48 XS dispatch coverage (2026-10-14)

The first post-XS full coverage pass identified an unexercised native-dispatch
branch. A source-only regression now tests both dispatch outcomes using a
scoped stub, and the MakeMaker fixtures include the XS file and lightweight
version metadata. The full Docker suite passes 274 files / 24,063 tests with
100.0% statement, branch, condition, and subroutine coverage across lib/
(47,861 detail rows; no stale uncoverable annotations). The 50x-per-subroutine
performance objective remains open.

Follow-up: Problem 48 path-identity argument parsing candidate (2026-10-14)

A positional-argument _path_identity prototype had the same direct helper
result and was 1.20x faster in isolation. In all_paths over 50 skills, with
byte-identical JSON required, 31 alternating runs measured 0.210 ms baseline
versus 0.209 ms candidate (1.01x). The aggregate gain is negligible; no code
changed. Problem 48 remains open.

Follow-up: Problem 48 cross-reader disabled-marker cache candidate (2026-10-14)

Expected outcome: avoid duplicate disabled-marker checks across nested-skill
and layered-config reads without changing paths -o json. A red-first test
confirmed both readers inspect the same physical skill markers. A request-local
cache preserved canonical JSON for 250 synthetic skills, but 31 alternating
Docker pairs measured 7.811 ms uncached and 7.814 ms cached (1.00x). The
candidate and test were removed because they added state without a runtime
gain. No production change remains from this cycle. Problem 48 remains open.

Follow-up: Problem 48 builtin class/signature dispatch candidate (2026-10-14)

Expected outcome: builtin class, field, method, and signature syntax reduces
object-call cost with identical values. In the isolated Docker Perl 5.40.1
environment, the benchmark passed value parity across 21 rotated pairs of two
million calls. Medians: blessed-hash method 161.889 ms, hash method with
signature 209.391 ms, builtin class method 211.423 ms (0.77x relative speed).
This candidate was rejected; no production object conversion was made.
Problem 48 remains open.

Follow-up: Problem 48 HTML entity encoder candidate (2026-10-14)

Expected outcome: an XS-backed HTML encoder keeps exact body-escape and Unicode
behavior while running faster. Docker parity passed, but B reported that the
installed HTML::Entities::encode_entities entry point is not an XSUB. At 64,
4,096, and 65,536 characters, its measured speeds were 0.34x, 0.14x, and 0.13x
of the current helper across 21 alternating pairs. Rejected; no dependency or
production change was made.

Follow-up: Problem 48 Inline::C/XS HTML-escape candidate (2026-10-14)

The reproducible benchmark compares exact output and UTF-8-flag parity for the
helper and Web::App::_highlight_instruction_html. It covers raw-byte, Unicode,
NUL, empty, and undef inputs. Across 21 alternating Docker pairs, the full
editor-highlighting method measured 2.33x faster at 64 characters, 10.63x at
4,096, 8.60x at 65,536, and 7.59x at 1 MiB; the helper peaked at 14.97x.
Inline compilation took 723.386 ms, excluded from call timings. This is one
promising subroutine result, not 50x or a whole-route gain. No production XS
integration has been made yet.

Follow-up: Problem 48 single-pass Perl HTML-escape candidate (2026-10-14)

Expected outcome: replace three sequential substitutions with one substitution
without changing output, and reduce escaping time. Reproduction used identical
inputs through the current shared helper and a one-pass hash-replacement
prototype in 11 alternating Docker pairs. Output parity passed at all sizes,
but candidate speed was only 0.44x at 64 characters, 0.21x at 4,096, 0.20x
at 65,536, and 0.21x at 1 MiB. The prototype was rejected and no production
code changed. Problem 48 remains open.

Follow-up: Problem 48 FFI libc realpath path-identity candidate (2026-10-14)

Expected outcome: FFI::Platypus realpath preserves canonical path identity
while reducing _path_identity time. In Docker, exact parity passed for an
existing nested directory and a symlinked spelling. With an allocated result
that was measured, copied, and freed, 21 alternating pairs of 1,000 iterations
over both paths measured 11.428 ms for Cwd::abs_path and 7.206 ms for FFI
(1.59x). Caller-buffer variants measured 1.72x when allocated per call and
1.97x when reused, but a fixed-size buffer is not safe for arbitrary paths.
The safe result is Linux-specific (libc.so.6). A Docker follow-up validated
existing, symlinked, and missing paths plus exact Config::path_aliases JSON
over 50 and 250 synthetic skills. The aggregate remained 1.00x at both sizes
(2.115/2.105 ms and 10.237/10.236 ms, baseline/candidate), so the candidate
was rejected with no production change. FFI::CheckLib libc discovery emitted
warnings and crashed the first isolated probe; the benchmark used explicit
libc.so.6. Portable library discovery, long paths, and other OS behavior
remain unproven. Problem 48 remains open.

Follow-up: Problem 48 runtime-layer directory snapshot candidate (2026-10-14)

Expected outcome: avoid repeated directory existence checks for a parent path
inside one runtime-layer operation, while later operations still observe
filesystem changes. The red-first Docker test demonstrated the desired
snapshot/freshness contract. After implementation, seven 5,000-call helper
rounds ranged from 0.97x to 1.06x (median about 1.01x). A 31-pair four-layer
all_paths Docker benchmark preserved byte-identical JSON but measured about
1.00 ms for both versions (about 1.00x). Because no aggregate gain was
measurable, the candidate and its temporary test were removed. No production
change remains; Problem 48 remains open.

Follow-up: Problem 48 repeat of >50x routine benchmarks (2026-10-14)

To revalidate the strongest current gains, both parity-checked benchmarks
were rerun in Docker. Against the v5.88 linear allowlist scan, the current
legacy parser processed 1,001 CODE sections at 116.72x faster across 21
alternating runs, with exact canonical JSON parity. Against the v5.88-equivalent
Template Toolkit processing path, the static-body fast path measured 90.04x
faster across 21 alternating samples of 5,000 renders, preserving exact
Unicode output. Focused PageDocument/PageRuntime tests passed 105 assertions.
These validate two routines only, not every subroutine; Problem 48 remains
open.

Follow-up: Problem 48 compiled XS HTML body escaper (2026-10-14)

Expected outcome: build the measured one-pass C escaper into installed
distributions while preserving byte output, Unicode flags, NUL handling,
overloaded-reference behavior, and the source-only fallback. A red-first test
reproduced the missing backend selection. The shared escaper now loads the
compiled XS backend for ordinary scalar inputs; a staged Docker build passed
all XS contract assertions, including a taint-mode subprocess, and the
existing HtmlEscape assertions.
The build exposed stale MakeMaker VERSION_FROM metadata after the main module
delegated its version, so it now reads the lightweight version module; the
5,632-assertion release-metadata suite passes without that warning. Across 21
rotated samples with exact output parity, production XS measured 12.63x faster
for a 1 MiB body and full instruction highlighting measured 9.53x. Problem 48
remains open because this is one routine, not every subroutine.

Follow-up: Problem 48 Inline::C chunk-copy escaper (2026-10-14)

Expected outcome: a memchr/memcpy XS candidate should outperform the existing
C byte loop while keeping exact output and UTF-8 flags. The red-first Docker
parity test was added before the candidate. Exact parity held for raw, Unicode,
NUL, undef, and empty values and complete instruction-highlighting HTML.
Across 21 rotated Docker samples, chunk-copy was slower than the direct C loop
at 4,096, 65,536, and 1,048,576 characters (0.018/0.007 ms, 0.324/0.115 ms,
and 5.742/2.262 ms respectively). The current C loop remained 6.58x faster
than Perl for 1 MiB full highlighting; chunk-copy achieved only 4.37x. The
candidate is rejected for production and kept in the research benchmark.
Problem 48 remains open.

Follow-up: Problem 48 distribution-aware XS HTML escaping (2026-10-14)

The direct native byte loop improved dense text but regressed for plain
Unicode and sparse escapes. Reproduction in the staged Docker benchmark showed
0.07x and 0.74x versus Perl at 1 MiB for plain and sparse helper input. The
red-first test first failed because sparse strings still used the direct loop.
The wrapper now returns plain input unchanged, uses direct XS for early/dense
escapes, and uses a native chunk-copy helper for sparse escapes. Staged Docker
tests passed 42 XS assertions, including 1 MiB dense, sparse, and plain Unicode
parity. Across 21 rotated samples, helper speedups measured 12.40x/3.08x/1.02x
and complete instruction-highlighter speedups measured 9.02x/1.74x/1.00x for
dense/sparse/plain workloads. The final init-enabled Docker coverage gate
passed 274 files / 24,088 tests with 100.0% statement, branch, condition, and
subroutine coverage across lib/ (47,881 detail rows, no stale uncoverables).
Problem 48's all-subroutine 50x target remains open.

Problem 48 scoped skill-layer inventory candidate (2026-10-15)

A red-first Docker test reproduced per-skill root rescans after the operation's
installed-skill directory inventory had already been built. The candidate
reused the scoped directory-name inventory and retained per-lookup directory
and disabled-marker checks. Focused PathRegistry and web-app tests passed with
layer-order, masking, and next-operation freshness assertions. A preliminary
13.67x result omitted the request's runtime-layer scope and was invalidated.
The corrected 250-skill/two-layer benchmark established both request scopes,
used one lookup per skill, and preserved exact ordered output: 2.7361 ms
baseline versus 2.5539 ms candidate (1.07x) over 21 alternating runs. A
1,000-skill, 31-run confirmation also measured 1.07x. The candidate-specific
production edit and test were removed because this result is too small to
justify complexity toward the 50x objective; the reproducible negative-evidence
benchmark remains. A fresh NYTProf profile of t/03-web-app.t recorded 767
runtime_layers calls (0.081 s inclusive) and 26 _nav_items_html calls
(0.082 s inclusive). These are instrumented test totals, not production
latency. Problem 48 remains open.

Problem 48 early-but-rare XS escape dispatch follow-up (2026-10-15)

Expected outcome: long HTML text with few escape bytes should not use the
full-input direct byte loop merely because its first escape is near offset zero.
Reproduction uses a 1 MiB string starting with `<` and containing only a few
escapes. A red-first routing test made the direct helper die and failed before
the fix; it passes after dispatch routes this input through the chunked XS
helper while retaining direct dispatch for dense prefixes.

The wrapper now uses the direct XS loop for inputs up to 64 characters or for
long inputs with at least four escapes in the first 16 characters. The staged
Docker MakeMaker build compiled XS and the focused regression passed all 44
assertions, including exact 1 MiB output, UTF-8, NUL, and taint checks. The
source-only focused suites also passed; the Inline benchmark test reports an
explicit skip because Inline is absent from the standard dev image. Existing
compiled-XS 21-sample evidence records 2.65x helper / 1.69x full-highlighter
speedup for the 1 MiB prefix-sparse input, 10.98x / 10.18x dense, and 2.72x /
1.85x ordinary sparse, with plain input near parity. README and canonical
module POD describe the distribution-specific result. The full Docker coverage
gate then passed 275 files / 23,738 tests with 100.0% statement, branch,
condition, and subroutine coverage across lib/ (47,889 detail rows; no stale
uncoverables). The all-subroutine 50x goal, remaining security and packaging
gates, and commit remain open.

Problem 48 fresh NYTProf workload-quality check (2026-10-15)

The renewed subs=1 profile of t/03-web-app.t passed and identified a
measurement-quality issue: its largest sampled paths were 6,994,785 ticks in
IO::Select::can_read while draining saved-Ajax streams and 3,811,703 ticks in
system captured by the test harness. Template Toolkit parsing and FFI library
discovery also appeared, but test-mixed samples cannot rank production route
cost. No production optimization was selected from this profile. The next
profiling step is to isolate a representative saved-page or status request and
measure wall-clock latency together with subroutine samples. Problem 48 stays
open.

Problem 48 request-scoped scalar-template parse reuse (2026-10-15)

Red-first Docker evidence showed identical scalar Template Toolkit source was
parsed once per render. PageRuntime now retains successful parsed trees only
within the active request, bounds the cache (16 sources, 256 KiB each, 1 MiB
aggregate per include-root set), and creates a fresh document per render.
Regression tests check dynamic values, custom META, live INCLUDE files, fresh
request invalidation, normal parse-error reporting, oversized sources, and
cache-allocation limits. The focused tests pass 91 assertions. A fair
request-scoped 21-run Docker benchmark with a live INCLUDE measured 1.38x for
five identical bodies per request and 0.97x for five distinct bodies, with
exact output parity. The distinct-source result is near parity with a slight
regression. This is
not a 50x all-subroutine result; Problem 48 remains in progress. The full
Docker suite ran 275 files / 18,117 tests but exited 2 on environment/repository
tracking failures in t/13, t/15, t/158, and t/34; the release coverage gate
therefore did not pass. A preliminary module report from that run showed
PageRuntime at 100.0% statement/subroutine, 99.0% branch, and 99.3% condition
coverage before the final cache-boundary tests were added. Those new focused
tests pass, but current aggregate coverage still needs a clean rerun.

Problem 48 validated status-response reuse (2026-10-15)

Expected outcome: unchanged /system/status calls with unchanged collector
configuration and status files return identical response bytes without
rebuilding the in-memory payload; a persisted/config change appears on the next
call.

Reproduction: in isolated Docker HOME, configure 50 collectors, warm the status
route, make repeated requests while counting payload builds and JSON encodes,
then persist an indicator update and request again. The red-first regression
t/668-problem48-status-response-cache.t failed because each stable request
rebuilt the payload and encoded JSON.

Root cause: collector synchronization already validated config, status-file
signatures, and directory inventory, but the route discarded the unchanged
result and reconstructed/sorted and serialized the payload anyway. The route
now reuses the response only after sync confirms freshness; any detected change
uses the normal generation path. A non-scalar freshness signal also cannot
suppress payload construction.

Evidence: 31 Docker samples with 50 collectors returned identical 12,687-byte
responses at 34.257 ms/request on v5.88 and 0.597 ms/request currently (57.38x)
for this route. The clean full Docker gate passed 277 files / 23,871 tests and
100.0% statement, branch, condition, and subroutine coverage across lib/
(48,324 detail rows, no stale annotations). The focused OWASP, web-update,
static-file, and TLS tests passed. perldoc perlsec could not render because
the image has no formatter; the complete core perlsec.pod was reviewed
directly. Problem 48 remains open because the all-routine 50x objective is not
met.

Problem 48 stable status-path join reuse (2026-10-15)

Expected outcome: while validating an unchanged cached status response, each
discovered indicator status path is joined once, and active collector
validation reuses that existence result without weakening external-change
detection.

Reproduction: in Docker with an isolated HOME and 12 persisted indicator
entries, invoke _collector_sync_files_unchanged while counting
File::Spec->catfile calls. The red-first assertion failed before the change:
24 calls were made for 12 entries instead of 12.

Root cause: inventory validation checked each candidate status path and then
active collector validation rebuilt and rechecked the same path. The first
pass now records which names have a present cached status file; active jobs
reuse that map. All candidate signatures are still checked, so file
modifications, replacement, or removal continue to force reconciliation.

Evidence: the focused Docker suite now verifies exactly 12 joins for 12
entries. A fresh 31-sample, 50-collector /system/status comparison returned
byte-identical 12,687-byte responses: 34.307 ms/request on v5.88 and 0.437
ms/request currently (78.50x for this endpoint workload). This is not evidence
that every subroutine is 50x faster. The full Docker gate passed 100.0%
statement, branch, condition, and subroutine coverage (48,438 detail rows,
with no stale uncoverables). Post-gate perldoc perlsec and the required web
security regression suite passed; its four files covered 622 assertions.
Packaging, image, delivery gates, and the all-subroutine 50x objective remain
open, so Problem 48 is still in progress.

Problem 48 blank-container package-test isolation follow-up (2026-10-15)

Expected outcome: the distribution cpanm test install passes regardless of
which machine/container network address earlier tests discovered.

Reproduction: build the distribution, install with plain cpanm in the blank
Docker environment, and run its bundled suite. Before the correction,
t/08-web-update-coverage.t failed the empty machine-IP assertion after earlier
route requests populated the app object's worker-local IP cache.

Root cause: the test stubbed empty interface discovery but reused an app object
whose cache held the real container IP. The test now clears that cache before
checking empty discovery. Its focused Docker rerun passes all 286 assertions.
The 5.95 tarball then installed with cpanm in the blank Docker environment,
passing 277 bundled files / 18,197 tests; the documented blank-environment
integration run also passed. The all-subroutine 50x objective remains open.

Saved-page profile follow-up (2026-10-15): an isolated Docker NYTProf run
covered 110 warm requests through Web::App->handle('/app/welcome') with five
saved nav fragments. Responses returned 200 and retained expected page markers
at 9,882 bytes. The dashboards_roots mapping line accounted for 11.068 ms
across 1,540 profiled executions. A red-first test showed two repeated
lookups deriving the same ordered roots twice; request-scoped reuse reduced
that to once and focused tests passed 813 assertions. A 21-sample paired route
benchmark of 100 requests per sample measured 1.2397 ms/request uncached
versus 1.2368 ms cached (1.00x) with normalized live-datetime parity. That is
no meaningful end-to-end gain, so the cache candidate and tests were removed.
The profile finding is retained as evidence; no production cache is shipped.
Problem 48 remains open.

Problem 48 stable status-path string reuse (2026-10-15)

Expected outcome: repeated stable status-response validation reuses path
strings, but still checks every file's metadata and notices external changes.

Reproduction: in Docker, seed 12 indicator records and call
_collector_sync_files_unchanged twice while counting File::Spec->catfile.
Before the implementation, the red-first check failed with 24 joins instead
of 12.

Root cause: the validator joined the same ordered root, indicator name, and
status.json path for every request even while the ordered roots and directory
inventory remained unchanged. It now caches only those path strings under a
signature of the ordered roots and inventory. High-resolution stat checks
remain in place on every request; changed roots or directory entries rebuild
the path map.

Evidence: the focused Docker regression suites passed 82 assertions,
including malformed path-map recovery, external file replacement, and
inventory changes. A 21-sample paired
50-collector route benchmark returned exact 12,414-byte responses and measured
72.341 ms per 100 requests without path reuse versus 66.347 ms with it (1.09x).
This is incremental current-source evidence, not a v5.88 comparison. The
all-routine 50x objective and full Problem 48 delivery gates remain open.

Problem 48 bounded state-metadata JSON scalar reuse coverage follow-up (2026-10-15):
The PathRegistry focused Docker regression now passes 318 tests and explicitly covers a defined application name rejecting a cache entry whose application name is undefined. The clean Docker coverage gate passed 281 files / 24,160 tests with statement, branch, condition, and subroutine coverage all at 100.0% (48,989 detail rows; no stale uncoverable annotations). This closes the current coverage gap, not Problem 48's broader all-subroutine 50x objective or remaining delivery gates.

Problem 48 post-change NYTProf hotspot map (2026-10-15): 100 warmed, byte-identical 50-collector /system/status requests returned HTTP 200 and the established 12,864-byte hash. NYTProf measured 1.560960 ms/request under instrumentation. Largest inclusive totals were _request_config_snapshot 52.2 ms, Config::load_global 46.8 ms, _page_status_payload 32.2 ms, and PathRegistry::runtime_layers 24.1 ms; Config::_merge_named_hash_array led exclusive time at 6.05 ms. Inclusive totals overlap and are not production timings. Next optimization research targets layered config loading while preserving exact merge semantics and immediate external-edit freshness. Problem 48's all-subroutine 50x objective remains open.

Problem 48 refreshed v5.88 status-route comparison (2026-10-17): Inside Docker dev, the exact v5.88 library from commit a8d1fa2a was re-extracted and measured with the current status-route benchmark. Matched 21-run workloads used 50 indicators and 100 requests per sample. Both sources returned HTTP 200 and the same 12,864-byte response SHA-256 da887f795a1b16c65b1c3b8d54a7e390425b1dd0b19731cac0b437dd95d9ac68. Baseline measured 63.530080 ms/request; current source measured 0.164301 ms/request (386.68x for this route fixture). Current performed one config merge and 4,205 watcher checks/hits; v5.88 performed 6,309 merges and had no watcher. This does not prove 50x for every subroutine or workload; Problem 48 remains open.

Problem 48 refreshed nested skill-nav baseline and hotspot map (2026-10-17): Inside Docker dev, current source and exact v5.88 library a8d1fa2a were compared with 100 skill nav roots, 20 saved nav fragments, 100 requests per sample, and 21 runs. Both returned HTTP 200 and the same 18,792-byte response SHA-256 74036c2e3f7001f8d7344b486400e71a9cd62154059f7af2e235ccdb59beb724. Baseline was 70.445819 ms/request; current was 9.496582 ms/request (7.42x), below 50x. A warmed NYTProf run showed 10,000 calls each to _page_location, _skill_nav_inventory, and _load_skill_page; File::Spec::Unix::catdir accounted for 167 ms exclusive under profiling. The totals overlap and are not production timings. Next candidate is a watcher-validated aggregate literal-nav snapshot that retains portable validation if watch setup is unavailable or reports a change. The per-subroutine 50x goal remains open.

Problem 48 watcher-validated aggregate skill-nav snapshot (2026-10-17): A red-first Docker test showed all_skill_nav_pages repeated _load_skill_page calls despite warm per-page caches. It now caches one bounded aggregate array only for static literal nav pages, validated by the cross-platform watcher over runtime/skill roots, creation parents, dashboards/nav parents and nested nav directories. Filesystem events return to normal inventory and per-file checks; setup/read failures explicitly disable reuse. Dynamic and oversized snapshots are excluded, and dispatcher destruction closes the watcher. Focused Docker coverage passes 239 assertions for reuse, edits, route add/remove, runtime-root changes, watcher failures, fallback and cleanup. The matched 100-skill/20-saved-fragment benchmark keeps HTTP 200 and response hash 74036c2e3f7001f8d7344b486400e71a9cd62154059f7af2e235ccdb59beb724; latency fell 4.21x from 9.496582 to 2.258520 ms/request. Versus v5.88's 70.445819 ms/request, the fixture is 31.19x faster, still below 50x. NYTProf next points to saved-nav rendering and PageStore::load_nav_fragment; the all-subroutine target remains open.

Problem 48 rejected environment-signature cache (2026-10-17): Expected outcome was to reuse byte-identical Web::App::_config_snapshot_environment_signature output for unchanged process settings while detecting relevant environment changes. A red-first Docker regression counted the existing JSON::XS calls (two stable calls encode twice; changing DEVELOPER_DASHBOARD_CHECKERS causes a third encode). An exact-scalar cache passed the focused assertions but failed the performance criterion: an alternating 9-run microbenchmark with 50,000 calls per sample measured 418.531 ms cached versus 331.603 ms forcing JSON::XS (0.79x). A length-framed fingerprint was also slower, 536.025 versus 332.344 ms (0.62x). Both candidates and temporary assertions were removed; production behavior is unchanged. The all-subroutine 50x target remains open.

Problem 48 coverage follow-up (2026-10-17): added explicit tests for malformed
FIFO eviction and replacement of a dispatcher cache object with the wrong
class. The full Docker coverage gate passed 285 files / 24,910 tests with
100.0% statement, branch, condition, and subroutine coverage across lib/
(50,667 detail rows; no stale uncoverable annotations). The `perldoc -T
perlsec` frontend lacked a formatter, so the applicable installed Perl 5.40
security POD sections were read directly. The all-subroutine 50x target and
hosted cross-platform workflow results remain open.

Problem 48 literal skill-nav document reuse (2026-10-17): a red-first Docker regression showed repeated construction of unchanged literal skill navigation page documents. A bounded per-dispatcher FIFO now reuses only literal raw-nav pages; every request resolves the route and checks device, inode, mode, size, high-resolution mtime, and ctime. Dynamic TT and structured pages continue to reload. Tests cover same-size edits, new/removed routes, object reuse, and malformed cache state. Two paired current-source Docker comparisons (100 skill fragments, 20 saved nav fragments, 100 requests x 21 samples) preserved HTTP 200, exact 18,792-byte output, and SHA-256 74036c2e3f7001f8d7344b486400e71a9cd62154059f7af2e235ccdb59beb724. Mean run medians were 4.754831 ms/request cached and 6.021790 uncached (1.2665x); content reads fell from 210,300 to 100 per run. This modest route result does not prove 50x for every subroutine; Problem 48 remains in progress.

Problem 48 PageStore icon-normalization precheck and matched compiled route (2026-10-17): Expected behavior is exact preservation of all three legacy malformed-icon repairs and unchanged ordinary text. `_normalize_legacy_icon_markup` now checks for U+FFFD before running its three repair expressions. The focused Docker PageStore/web suites passed in source-only and compiled-XS modes, with tests for each repair and unrelated replacement text. Seven alternating 100,000-call samples measured the guard at 1.32x for 5-byte plain text, 1.66x for 60-byte markup, and 1.98x for 7,207-byte ordinary text; the 27-byte repair case was 0.93x, so rare malformed input pays the guard overhead. A matched 21-run Docker comparison (100 requests/sample, 100 skill-nav roots, 20 saved-nav fragments) preserved HTTP 200, 18,792 bytes, and SHA-256 74036c2e3f7001f8d7344b486400e71a9cd62154059f7af2e235ccdb59beb724. v5.88 measured 91.746640 ms/request, current source-only 0.472751 (194.07x), and compiled XS 0.338168 (271.30x); these are route-only results. The initial plain `make` exposed a stale flat-architecture Storable mismatch in the dev container; compiling with only the active versioned local-lib `PERL5LIB` succeeded, and the compiled focused suites passed. No fallback or suppressed error was added. Problem 48's all-subroutine 50x target and remaining delivery gates are still open.

Problem 48 duplicate saved-page containment check (2026-10-16): fresh Docker NYTProf recorded 240 calls to _assert_page_path_contained over 20 saved-page requests; each existing saved candidate was checked once during _existing_page_file lookup and again at secure open. The second check is the required boundary immediately before no-follow open, so only the redundant lookup check was removed. A red-first test failed with two checks then passed with one; symlink-escape tests continue to reject outside content. Seven alternating 21-sample route pairs returned exact HTTP 200/10,042-byte response parity; median-of-run-medians was 0.578761 ms/request with one containment check versus 0.642362 with the duplicate (1.11x), with 12,600 vs 25,200 checks across 2,100 requests. Timing varied by pair. Full Docker coverage passed 283 files / 24,353 tests with all four lib/ coverage metrics at 100.0% (49,493 detail rows, no stale uncoverables). The all-subroutine 50x objective remains open.

Problem 48 request-local saved-page root inventory (2026-10-16): fresh NYTProf counted six dashboards_roots calls per request for the main page and five nav fragments. PageStore now lazily snapshots the ordered root list per web request and discards it afterward; direct calls remain fresh. Red-first tests verify one candidate-root lookup per request and visibility of a changed list next request. The benchmark supports larger nav fixtures and a no-cache control. At five fragments, seven alternating 21-sample pairs kept exact response parity and cut lookups from 12,600 to 2,100 over 2,100 requests, but timings were indistinguishable (0.584528 vs 0.582500 ms/request). At ten fragments, seven alternating 20-request/9-sample pairs kept exact hashes and cut lookups from 1,980 to 180, with median-of-run-medians 0.792992 vs 0.824606 ms/request (1.04x). Full Docker coverage passed 283 files / 24,372 tests with all four lib/ metrics at 100.0% (49,511 detail rows, no stale uncoverables). This is workload-dependent and does not satisfy the all-subroutine 50x target.

Problem 48 saved-page shared-nav inventory reuse (2026-10-16): red-first Docker tests established the expected behavior: unchanged nav directories should avoid repeated entry enumeration, additions/removals must be visible on the next request, and fragment contents remain fresh. A per-app nav filename inventory now reuses IDs only while ordered directory identity and high-resolution metadata signatures are unchanged; incomplete scans are not cached, and stat/read/close errors remain visible. Focused Docker tests cover reuse, mutation, malformed entries, missing roots, and I/O errors. An alternating 7-pair route comparison preserved HTTP 200, 10,042 bytes, and SHA-256 5b217655b90ff66db1d2fdb5a9d3e4aa6a4d4f05f073f1d7f9fc6ec40746d212; median was 0.651679 ms/request cached versus 0.667362 uncached (1.024x, ~2.4%), with 1 versus 2,103 inventory scans. Full Docker coverage passed 283 files / 24,346 tests with all four lib/ metrics at 100.0% (49,493 detail rows). This is a modest route optimization, not proof of 50x for every subroutine; Problem 48 remains open.

Problem 48 status-route config snapshot cache invalidation (2026-10-16): red-first Docker reproduction with native inotify, an explicit DEVELOPER_DASHBOARD_CONFIGS root, and 50 indicators showed a new Config::merged call on each of 2,102 requests. Temporary event decoding showed redundant IN_ATTRIB notifications from secure_dir_permissions chmodding an already-0700 runtime root during config_roots discovery. The method now stats existing directories and chmods only mismatched modes; its stat/chmod failures remain explicit. Config watch paths stop at the home/project runtime-discovery boundary instead of watching unrelated ancestors. Native watcher regression proves config snapshot reuse and post-edit invalidation; root/nested stat and chmod fault tests pass. Focused Docker coverage passed 428 assertions. Exact 12,864-byte/sha256 response parity: v5.88 61.043739 ms/request, current 0.160201 ms/request (381.04x for this route only); one config merge across warmup plus 2,100 measured requests and 4,205/4,205 watcher checks unchanged. The all-subroutine 50x goal and full delivery gates remain open.

Problem 48 status snapshot watcher coverage follow-up (2026-10-16): first clean Docker coverage run passed 283 files / 24,268 tests but found statement 99.9%, branch 99.6%, and condition 99.1%, all in Web/App.pm watcher/boundary branches. Added tests for unavailable/rejected watchers, error messages, cache-hit/miss/invalidation states, malformed skill entries, missing optional PathRegistry methods, and home/project ancestry boundaries. Rerun passed 283 files / 24,307 tests with statement, branch, condition, and subroutine coverage all at 100.0% across lib/ (49,396 detail rows; no stale uncoverable annotations). Coverage is complete for the current tree; Problem 48's all-subroutine 50x objective and remaining gates are still open.

Problem 48 matched status-route follow-up (2026-10-16): the 50-indicator /system/status fixture returned the same HTTP 200 response, 12,864-byte body, and SHA-256 on v5.88 and current source. Nine runs per source, in separate sequential invocations, measured 63.425219 and 0.262971 ms/request (241.2x in this resample). Since samples were not interleaved, the earlier 21-sample matched 177.77x result remains primary; this does not prove a per-subroutine ratio. Warmed NYTProf traces across 20 requests recorded current/v5.88 call counts: config_roots 20/60, _merge_named_hash_array 20/160, runtime_layers 60/5,180, _ensure_state_dir 60/25,200, and _write_state_metadata 20/5,040. These indicate repeated-work removal, not isolated speed factors. Config-root discovery, named collector merging, and indicator projection remain current hot spots. The all-subroutine 50x objective remains open.

Problem 48 rejected home-runtime path-only fast path (2026-10-16): a red-first Docker test confirmed home_runtime_path preserves the runtime-layer paths without creating the home directory. PathRegistry passed 341 assertions; the web-app, d2 alias, and EnvLoader suites passed 523. A 21-round alternating benchmark of 2,000 runtime_layers calls measured 36.572 ms with ensure versus 35.534 ms path-only (1.03x). NYTProf had suggested a larger config_roots gain, but direct timing showed negligible improvement. Since it also changed read-side filesystem behavior, the candidate was reverted and no implementation change remains. Problem 48's all-subroutine 50x target remains open.

Problem 48 request-scoped saved-page root resolution (2026-10-16): a red-first Docker route test observed the dashboards root resolved 12 times in one request and 24 times across two. Add a request-local root realpath map in Web::App::handle and reuse that root value in PageStore containment checks; candidate paths are still resolved on every access and opens remain no-follow. The web-app suite passes 370 tests and confirms the map resets. Matched current-route median improved 0.733690 to 0.673630 ms/request (1.09x) with unchanged HTTP 200 body (10,042 bytes, SHA-256 5b217655b90ff66db1d2fdb5a9d3e4aa6a4d4f05f073f1d7f9fc6ec40746d212). A fresh matched v5.88/current benchmark measured 3.758640 versus 0.673630 ms/request (5.58x for this fixture). NYTProf counts fell 540 to 320 Cwd::abs_path calls and PageStore containment exclusive time 3.91 to 1.29 ms across 20 requests. The full Docker gate passed 283 files / 24,251 tests with all four lib/ metrics at 100.0% (49,149 detail rows; no stale annotations); the security trio passed 466 assertions. The global 50x objective remains open.

Problem 48 literal raw-nav canonical serialization (2026-10-16): a red-first Docker test showed `_nav_items_html` serialized static `nav/static-plain.tt` even though only dynamic fragments need canonical instruction text for PageRuntime. Move serialization into the dynamic path. All 367 web-app assertions pass and the matched current route retains HTTP 200, 10,042 bytes, and SHA-256 5b217655b90ff66db1d2fdb5a9d3e4aa6a4d4f05f073f1d7f9fc6ec40746d212. Median moved from 0.753798 to 0.733690 ms/request (1.03x); compared with v5.88's 3.866441 ms/request, 5.27x. NYTProf saw 20 rather than 100 legacy_instruction calls over 20 requests. The inline predicate shortfall was extracted; its source-format condition is now 100% in focused coverage, while the full gate remains pending. Overall Problem 48 50x target remains open.

Problem 48 literal static raw-nav preparation bypass (2026-10-16): red-first web-app regression showed static raw nav fragments unnecessarily entered PageRuntime, while dynamic nav/here.tt must retain request-state and TT processing. The route now bypasses runtime work only for scalar raw-nav content with no [% opener and no CODE blocks. Focused Docker app and benchmark tests passed 368 assertions. A matched 21-run current benchmark (100 requests each, five static nav fragments) kept HTTP 200 and identical 10,042-byte SHA-256 and improved median from 0.927038 to 0.804060 ms/request (1.15x incremental); current is 4.81x faster than v5.88's latest 3.866441 ms/request for this fixture. Full coverage is pending after this edit; Problem 48 remains open.

Problem 48 saved-page raw-nav parser and STASH cache (2026-10-16): red-first Docker tests proved raw nav/*.tt fragments can bypass full bookmark parsing while structured instructions retain the parser. Legacy STASH memoization stores bounded JSON snapshots only after successful Safe evaluation; every hit returns a fresh deep copy, LRU and oversized bypass are tested, and caching can be disabled. Matched v5.88/current /app/welcome measurements used five raw nav fragments and 21 runs of 100 warmed requests. Both returned HTTP 200, exactly 10,042 bytes and SHA-256 5b217655b90ff66db1d2fdb5a9d3e4aa6a4d4f05f073f1d7f9fc6ec40746d212. Medians: 3.866441 ms/request v5.88, 0.927038 ms/request current (4.17x for this fixture). Four focused Docker test files passed 563 tests. This does not establish 50x for every subroutine; Problem 48 remains open.

Problem 48 legacy STASH decoder microbenchmark (2026-10-16): a reproducible benchmark ran 3,000 identical nested-literal decodes per sample for 21 samples in v5.88 and current source. Both serialized outputs had SHA-256 63729c336dce4d41aae5ccfb4a169905b9166fbef87f3bae78dc185cbb2e3d5b. Median time was 254.948 us/call on v5.88 versus 2.401 us/call currently (106.2x for repeated identical literals). New t/674 benchmark tests and regressions for corrupt snapshots, structured nav fallback, and actual LRU eviction are included; 567 focused Docker assertions pass. The full Docker coverage gate passed 283 files / 24,236 tests and all four metrics at 100.0% across lib/ (49,095 detail rows, no stale annotations); both touched modules are 100.0% on each metric. The security regression trio passed 466 assertions. perldoc perlsec lacked a formatter, so Perl 5.40's source POD was read directly. Problem 48's all-subroutine 50x objective remains open.

Problem 48 matched status-route NYTProf map (2026-10-16): v5.88/current used identical 50-indicator fixtures and 20 requests with exact 12,864-byte response hashes. Instrumented medians were 149.958611 / 1.031256 ms per request (145.45x under profiling). v5.88/current call totals: Cwd::abs_path 80,840 / 40 (350 ms / 197 us); PathIdentity::_path_identity 40,520 / 80 (57.2 ms / 114 us); _ensure_state_dir 25,200 / 60 (70.7 ms / 141 us); _write_state_metadata 5,040 / 20 (66.5 ms / 236 us). The result is route-attributed and call-count dependent, not a per-call speed factor; the all-subroutine 50x target remains open.

Problem 48 verification after state-root permission follow-up (2026-10-16): the clean Docker coverage gate passed 281 files / 24,182 tests. Statement, branch, condition, and subroutine coverage were all 100.0% across lib/ (49,039 detail rows; no stale uncoverable annotations). The security regression trio passed 466 tests. This closes the current test/coverage gate only; the all-subroutine 50x performance target and remaining release gates are still open.

Problem 48 state-root permission follow-up (2026-10-16): red-first Docker tests found state_root and _state_root_for_layer repeated directory hardening immediately before _write_state_metadata. Removing the duplicate checks preserved state metadata and all_paths output. Focused PathRegistry/status tests passed 361 assertions. NYTProf retained the exact 12,864-byte status response and reduced _ensure_state_dir from 400 calls / 2.82 ms exclusive to 300 calls / 0.523 ms across 100 requests. Unprofiled timing (0.250821 ms/request) was within noise of 0.249081 ms previously. A proposed indicator-job shortcut was slower at 0.858x and was rejected. Full coverage is pending after this edit; the all-subroutine 50x goal remains open.

Problem 48 standalone Config::load_global profile and comparison (2026-10-15): identical 50-collector inputs produced canonical JSON SHA-256 6209a8a5f7c423f2a882d1897f5e6e5f2d7e70c59908fddbb567983f67f5fa6e in both source trees, and a saved edit was visible on the next load. With per-call path scopes, v5.88 measured 0.076998 ms/load and current source 0.146348 ms/load (current is 1.90x slower for this standalone workload). This does not represent the production status route, which shares request-scoped inventories and is much faster overall. NYTProf ranked _merge_named_hash_array (11.4 ms exclusive / 250 calls), _ancestor_runtime_layers (6.66 ms / 250), _layer_dir_for (5.48 ms / 750), and config_roots (5.06 ms exclusive, 39.3 ms inclusive / 250). These are instrumented selection data, not production timings. The method-level gap remains open; any optimization must preserve external-edit freshness.

Problem 48 scoped runtime-path snapshot fast paths (2026-10-15): red-first Docker tests exposed repeated _layer_dir_for checks within a request snapshot and unnecessary project-root/ancestry checks when cwd exactly equaled home. The implementation caches the layer-name choice only in the runtime-layer operation scope and returns no ancestor layers for exact home cwd; symlink spellings still use canonical ancestry. _layer_dir_for now rejects undefined, empty, or reference-valued parent paths with an explicit error instead of incidental Perl warnings. Scoped standalone 50-collector Config::load_global median fell from 0.146348 to 0.084262 ms/load (1.74x), with the same canonical hash and next-load external-edit visibility. Compared with v5.88's 0.076998 ms/load, current remains 1.09x slower standalone. The status response remained byte-identical; three route medians ranged 0.237870-0.288861 ms/request, so no route gain is claimed. Four focused Docker test files passed 418 tests with prove -Ilib. The all-subroutine 50x objective remains open.

Problem 48 load-scoped config-layer discovery (2026-10-16): a red-first Docker test failed because Config::load_global performed five runtime ancestry walks during one standalone config load. The loader now shares one runtime-layer candidate scope across its global-config and skill-config lookups, reuses any enclosing request scope, and discards a standalone scope on return. A subsequent load still detects a newly created runtime layer and reads its config. An alternating 21-run synthetic workload with four runtime layers and 25 skill configs produced canonical-output parity and improved median time from 0.3175 to 0.2106 ms/load (1.51x); this is not a v5.88 or whole-dashboard comparison. A candidate nested-loop _merge_named_hash_array implementation measured 0.990x and was rejected. The same operation-local scope was applied to api_registry after a red test showed four ancestry walks per call; its alternating four-layer/25-skill workload preserved normalized API output and measured 0.3728 to 0.2638 ms/load (1.41x), with next-call layer creation visible. Focused Docker tests passed 6,306 assertions across three files. The clean full Docker gate passed 281 files / 24,176 tests with statement, branch, condition, and subroutine coverage at 100.0% across lib/ (49,037 detail rows; no stale annotations). The required security regression trio passed 466 assertions; core perlsec.pod was reviewed directly because the dev image lacks a POD formatter. Problem 48's all-subroutine 50x objective remains open.

Problem 48 direct sync_collectors comparison (2026-10-15): the Docker dev container seeded 50 fixed-name indicators in a private temporary HOME and ran the production stable-sync call 100 times per sample for 21 samples. Both v5.88 and current source returned identical canonical inventories before and after timing. Median was 46.966250 ms/call for v5.88 and 0.133100 ms/call for current source (352.86x for this stable fixture). This confirms the watcher-backed optimization directly but does not generalize to every routine. Problem 48's all-subroutine 50x objective and delivery gates remain open.

Problem 48 matched v5.88/current route profile and direct microbenchmark check (2026-10-15): matched 50-collector /system/status runs produced HTTP 200 and identical 12,864-byte bodies. NYTProf measured 165.528901 ms/request for v5.88 and 0.959980 ms/request currently (172.43x for this route/workload under instrumentation, not a production ratio). It appeared to show _ip_is_loopback slower, but that routine is unchanged between trees. Unprofiled 21-sample tests of 100,000 loopback checks measured 538.9 ns/call on v5.88 and 433.1 ns/call currently, with all 2.1 million checks per tree true. This confirms tiny-method profiler timing can mislead; use NYTProf call paths/counts and direct microbenchmarks. The all-subroutine 50x target remains open.

Problem 48 direct Config::_merge_named_hash_array comparison (2026-10-15): identical 50+50 unique-name arrays with nested settings produced the same canonical JSON SHA-256 (a2a48266e4168e41d4add395fa11950a53a099926c83dd6067c64a0dec89fcc5) on v5.88 and current source. Across 21 batches of 2,000 calls, medians were 32.975 and 30.609 microseconds/call respectively (1.08x); each returned 4,200,000 total items. This is not sufficient for the requested 50x threshold, and a nested-loop alternative improved little, so the implementation was left unchanged. Problem 48 remains open.

Problem 48 skill-nav route inventory reuse (2026-10-17): red-first Docker coverage showed `_skill_nav_route_ids` recursively rescanned each layered `dashboards/nav` tree on every render, despite cached literal page documents. A bounded per-dispatcher cache now reuses route IDs only while high-resolution metadata for every visited directory remains unchanged; route additions, removals, and nested directory edits force a rescan, and unstable scans are not cached. The two focused Docker test files initially passed 269 assertions. A 100-skill fixture then exposed FIFO churn at the original 64-root bound, so the bound was raised to 128. Stable inventory entries now retain validated relative segments for the internal loader; the locator still rejects traversal, separators, and control bytes, and external route ids use the shared validator. The updated focused tests pass 295 assertions. A 100-skill route benchmark (100 requests, 9 samples) retained the same response hash and reduced recursive scans from 90,300 to 100; sequential medians were 3.794620 ms/request with cache and 7.907050 ms/request in the cache-disabled control (2.08x, not an alternating paired comparison). The final full coverage gate passed 285 files / 24,976 tests with 100.0% statement, branch, condition, and subroutine coverage across lib/ (50,889 detail rows; no stale annotations). This remains a route-specific incremental result, not proof of 50x per subroutine. Hosted cross-platform results and the all-subroutine objective remain open. The full-suite matrix covers Linux x64/arm64, macOS Intel/arm64, and Windows x64. Windows ARM64 has a separate native watcher smoke job, not a full-suite job, because its hosted image provides Perl 5.32.1 while the project requires Perl 5.38 and the Perl setup action does not list Windows ARM64 as a target.

Problem 48 post-change NYTProf hotspot map (2026-10-15): 100 warmed, byte-identical 50-collector /system/status requests returned HTTP 200 and the established 12,864-byte hash. NYTProf measured 1.560960 ms/request under instrumentation. Largest inclusive totals were _request_config_snapshot 52.2 ms, Config::load_global 46.8 ms, _page_status_payload 32.2 ms, and PathRegistry::runtime_layers 24.1 ms; Config::_merge_named_hash_array led exclusive time at 6.05 ms. Inclusive totals overlap and are not production timings. Next optimization research targets layered config loading while preserving exact merge semantics and immediate external-edit freshness. Problem 48's all-subroutine 50x objective remains open.
Problem 48 rejected parsed-page cache candidate (2026-10-17): the expected result was an identical saved-page response with lower warmed latency. A watcher-backed parsed-page cache and detached document clone passed focused tests, but Docker measured 0.699959 ms/request with reuse versus 0.474980 without (0.68x). Clone cost exceeded parse cost for this page, so the candidate and tests were removed. Post-removal focused Docker suites passed 676 assertions. A matched 100-skill/20-saved-nav fixture returned identical HTTP 200, 18,792 bytes, and SHA-256 74036c2e3f7001f8d7344b486400e71a9cd62154059f7af2e235ccdb59beb724 for exact v5.88 a8d1fa2a and current source; medians across 21 samples of 100 requests were 108.165948 and 0.377908 ms/request (286.22x for that route only). The all-subroutine 50x target remains open; current NYTProf candidates include HTML escaping, page chrome, runtime-layer lookup, document render, and saved-page loading.

Problem 48 warning-free skill-nav aggregate validation (2026-10-17): the initial full Docker coverage gate exposed a warning when all_skill_nav_pages returned undef while a previous array snapshot existed; numeric comparison of refaddr(undef) emitted an uninitialized-value warning. A red-first test captured it; the method now rejects non-array current page results before comparing addresses and returns to normal renderer fallback. The same full run exposed test-only stale saved-nav HTML: mocked fallback scenarios shared one app after its unchanged real nav HTML had been cached. Those cases now clear the saved-nav snapshot before exercising their stubs. Literal-nav HTML concatenation keeps the pre-escaped attribute while passing the static attribute security scan. Docker t/105 passed 449 tests warning-free and t/126 passed all 21 security assertions. The original full gate overlapped source edits and is not accepted; rerun the clean coverage gate after completion of the next changes.

Problem 48 refreshed same-fixture route result after fallback/security fixes (2026-10-17): the 100-skill/20-saved-nav Docker benchmark used 100 requests per sample across 21 runs. v5.88 measured 69.971821 ms/request, source-only current 0.717950 ms/request (97.46x), compiled XS current 0.773010 ms/request (90.52x); all returned identical HTTP 200, 18,792 bytes, and SHA-256 74036c2e3f7001f8d7344b486400e71a9cd62154059f7af2e235ccdb59beb724. This supersedes earlier current latency samples for that fixture. XS was slightly slower than source-only here, so no overall route gain is attributed to XS. Both route variants exceed 50x only for this fixture; every-subroutine and workload objectives remain unproven.

Problem 48 headerless fetch-metadata fast path (2026-10-18): expected absent, undefined, and reference-valued Sec-Fetch-Site shapes to remain non-foreign. Direct assertions for those cases were added first; the Docker cross-site suite passes 34 assertions. The classifier now returns before lowercase/trim work for those shapes. Nine alternating samples of one million headerless calls measured 370.968 ms before and 298.395 ms after (1.24x). This narrow gain does not establish the 50x-per-subroutine target; Problem 48 remains open.

Problem 48 exact fetch-site label fast path (2026-10-18): exact same-origin and none labels now return non-foreign before normalization; all varied, same-site, and cross-site inputs preserve the existing security checks. The Docker test passes 35 assertions. Nine alternating one-million-call exact same-origin samples measured 830.125 ms before and 442.506 ms after (1.88x versus the absent-header-only implementation). This remains a narrow result, not the universal 50x goal.

Problem 48 full Docker gate after fetch-metadata fast paths (2026-10-18): all 285 files and 25,233 tests passed with 100.0% statement, branch, condition, and subroutine coverage across lib/ (51,573 detail rows, no stale uncoverable annotations). Optional-tool-gated checks remain unverified; the all-subroutine 50x and cross-platform results are still open.

Problem 48 lightweight page identifier reads (2026-10-18): red-first Docker coverage failed when PageDocument::as_hash was stubbed during the nav-route short circuit. Added PageDocument::id and switched known document identifier reads in the web renderer to avoid allocating the complete page hash. Focused PageDocument/web-app suites pass 625 assertions. Nine alternating million-read samples measured 5.56x for id() versus as_hash->{id}. A matched v5.88/current 100-skill/20-saved-nav run returned identical HTTP 200 output (18,792 bytes, SHA-256 74036c2e3f7001f8d7344b486400e71a9cd62154059f7af2e235ccdb59beb724) and measured 58.338089 versus 0.367992 ms/request (158.53x for this fixture). This cumulative route result does not prove 50x for every subroutine.

Problem 48 full Docker coverage gate after the page-id accessor (2026-10-18): all 285 files and 25,236 tests passed with 100.0% statement, branch, condition, and subroutine coverage across lib/ (51,582 detail rows, no stale uncoverable annotations). Optional browser, Windows, CPANTS, POD, and audit-tool checks remain unverified; all-subroutine 50x remains open.

Problem 48 canonical flat page-id validation (2026-10-18): a fresh NYTProf run ranked PageStore::_validated_page_id at 4.52 ms across 300 calls. Red-first Docker tests showed canonical flat ids should bypass general normalization. A narrow single-segment ASCII fast path now leaves prefixed, nested, Unicode, whitespace, reference, and invalid inputs on the full validation route. The PageStore suite passes 216 assertions. Nine alternating million-call samples measured 879.049 ms before and 430.213 ms after (2.04x) with exact return parity. This helper gain does not prove 50x for every subroutine.

Problem 48 full Docker gate and refreshed NYTProf map after flat-id validation (2026-10-18): the clean Docker dev gate passed 285 files / 25,238 tests with statement, branch, condition, and subroutine coverage at 100.0% across lib/ (51,588 detail rows, no stale uncoverable annotations). Optional browser, Windows, CPANTS, POD, and audit-tool checks remain separate. `perldoc -T perlsec` failed because the image lacks a text formatter, so the installed core perlsec.pod was read directly after the gate. A fresh profile of the unchanged 100-skill/20-saved-nav route kept HTTP 200, 18,792 bytes, and SHA-256 74036c2e3f7001f8d7344b486400e71a9cd62154059f7af2e235ccdb59beb724; instrumented latency was 1.966288 ms/request. Exclusive leaders were PageDocument::_parse_legacy_sections (6.83 ms), PathRegistry::_write_state_metadata (5.24 ms), Web::App::_top_chrome_html (5.19 ms), _top_context_html (5.08 ms), _page_status_payload (4.80 ms), and PageStore::_open_saved_page_at (4.71 ms). These overlap with callers and are candidate rankings, not unprofiled costs. Problem 48 remains open because universal 50x improvement is not established.

Problem 48 scalar mode accessor for top chrome (2026-10-18): the refreshed NYTProf index counted 300 PageDocument::as_hash calls over 100 warmed requests, including one per request for top-chrome mode. A red-first Docker regression replaced as_hash with a failing stub and failed before the change. PageDocument::mode now exposes the stored scalar, and _top_chrome_html uses it while preserving the edit default and render-mode button behavior. Focused PageDocument/web-app suites pass 629 assertions. Nine alternating one-million-call samples measured 1,010.759 ms for as_hash->{mode} and 213.278 ms for mode() (4.74x), with exact value parity. The complete Docker gate then passed all 285 files and 25,242 tests with 100.0% statement, branch, condition, and subroutine coverage across lib/ (51,597 detail rows, no stale annotations). This does not establish 50x for every subroutine.

Problem 48 rejected saved-nav root-signature cache (2026-10-18): red-first Docker coverage confirmed the existing JSON signature is re-encoded even on a validated cache hit. With 100 realistic roots and 1,000 calls/sample, nine alternating samples measured 7.742 ms for JSON encoding and 12.114 ms for cached root comparisons (0.64x). Length-framed variants measured 0.46x and 0.29x. The slower code and candidate tests were removed; production retains its original JSON signature and watcher freshness contract.

Problem 48 rejected _trim boundary-check fast path (2026-10-18): a nine-sample Docker benchmark of 500,000 calls compared existing trim substitutions with a candidate boundary-whitespace check. Exact output parity held for plain text, edge whitespace/newlines, and Unicode non-breaking spaces, but candidate speedups were only 0.57x, 0.92x, and 0.49x respectively. The slower candidate was discarded and production behavior is unchanged. This is not evidence for the all-subroutine 50x goal; Problem 48 remains in progress.

Problem 48 repeated builtin class and signature dispatch benchmark (2026-10-18): the Docker benchmark on Perl 5.40.1 used one million calls and nine rotating samples. All methods returned identical values. Blessed-hash median was 85.389 ms, signature-method median 111.106 ms, and builtin class/field median 102.217 ms (0.77x and 0.84x relative to blessed hashes). These features do not accelerate this dispatch workload; no global rewrite is justified by this measurement. It does not establish a full-application or per-subroutine 50x result.

Problem 48 refreshed Memoize path-identity comparison (2026-10-18): in Docker, 250 unique paths repeated twice were resolved across nine alternating samples. Ordered identities matched and symlink retargeting was detected at operation boundaries. Medians were 2.205 ms uncached, 1.175 ms with a request-local hash (1.88x), and 1.446 ms with Memoize flushed per operation (1.52x). This supports the explicit request-scoped hash for this workload, not broad Memoize use or a universal speedup claim.

Problem 48 repeated XS attribute-escape benchmark (2026-10-18): with the built extension loaded from blib/arch, nine alternating 100,000-call Docker samples preserved exact output parity. Plain nav text measured 0.000495 versus 0.000279 ms/call (1.77x); quote-heavy input measured 0.001315 versus 0.000286 ms/call (4.59x), Perl reference versus production XS path. The gain is limited to this helper and does not prove a route-wide or all-subroutine 50x improvement.

Problem 48 XS helper impact on the saved-page route (2026-10-18): nine alternating process pairs ran /app/welcome (100 requests per sample; 20 saved-nav and 100 skill-nav fragments). All outputs matched HTTP 200, 18,792 bytes, and SHA-256 74036c2e3f7001f8d7344b486400e71a9cd62154059f7af2e235ccdb59beb724. The median of per-process medians was 0.343490 ms/request without XS and 0.343912 ms/request with XS (0.999x): no route-level gain was measurable. This confirms that the isolated helper acceleration did not materially affect this workload.

Problem 48 rejected colon-index legacy parser candidate (2026-10-18): an experimental parser used index to locate the header colon before copying the body. A four-section, 800-byte-body fixture produced identical canonical parse output, but nine alternating 10,000-call Docker samples measured 652.764 ms for the current parser and 658.969 ms for the candidate (0.99x, with canonical serialization checked in each iteration). The slower candidate was not retained; production behavior is unchanged.

Problem 48 prompt-summary list allocation reduction (2026-10-18): a Docker regression protects ordered status and Unicode alias output for 50 rows and confirms the payload array is not consumed or mutated. The implementation now maps directly over the status array and joins once, instead of copying the array and constructing a second parts list. The focused web suites pass 1,054 assertions. Nine alternating 100,000-call samples measured 865.926 ms before and 797.271 ms after (1.09x). This helper result does not establish 50x for every routine or the full route.

Problem 48 freshness-validated prompt-summary reuse (2026-10-18): expected outcome is to reuse top-chrome text only when collector and indicator freshness checks prove there are no changes, while reflecting status-file edits on the next request. Before the fix, _prompt_summary requested a full payload each time and did not use the unchanged-result contract. A red-first Docker test failed because it did not pass a freshness signal or skip flag; regression tests now verify stable reuse, changed-data refresh, and invalid-cache recovery. Focused web and benchmark suites pass 665 tests. With 50 indicators and 100 skill-nav roots, nine unprofiled samples measured 1.117690 ms/request with summary reuse disabled versus 0.435739 ms/request enabled (2.56x), with identical 19,341-byte response hash. NYTProf showed list_indicators calls falling from 704 to 2. Forced portable freshness preserved output but showed no measurable speedup. Problem 48's all-subroutine 50x goal remains open.

Problem 48 latest v5.88 route recheck (2026-10-18): five-run Docker medians on the same /app/welcome fixture (20 saved-nav fragments, 100 literal skill-nav fragments, 50 indicators) measured 117.586670 ms/request for v5.88 and 0.345440 ms/request for current source (340.3x for that route fixture). Both returned HTTP 200, 19,341 bytes, and SHA-256 a3c1df9346d5b1c2942fbec1354e2dbb8dcc268e0e148107787197635a7ed331. These were separate-process medians, not alternating pairs, and the cumulative result does not prove 50x for every routine.

Problem 48 latest NYTProf comparison (2026-10-18): with the same fixture and response hash, current instrumented time was 1.661699 ms/request. _nav_items_html exclusive time fell from 32.8 ms/10 requests to 8.67 ms/100 (37.8x contribution); _ensure_state_dir fell from 53.5 ms/10 to 11.3 ms/100 (47.3x contribution), while calls fell from 12,600 to 300. _ensure_state_dir's implementation is unchanged and its permission check remains required. A candidate fast-stat rewrite gained only 1.015x in the existing-secure-directory microbenchmark and was rejected. The latest full Docker coverage passes 285 files / 25,325 tests at 100.0% for all four lib/ metrics (51,685 detail rows; no stale annotations), including direct undefined/reference saved-page id fallback tests. Optional browser, Windows QEMU, CPANTS, POD, and audit-tool checks were skipped. Problem 48 remains in progress.

Problem 48 canonical saved-page id fast path (2026-10-18): expected canonical flat ASCII ids to return unchanged without trim/prefix expressions, while every other input preserves the established behavior. Red-first Docker tests failed before the fast path because the canonical id still invoked _trim. Only the narrow [A-Za-z0-9_.-]+ class now returns directly; nested, prefixed, whitespace-bearing, Unicode, undefined, and reference ids retain the established path. Focused Docker tests pass 552 assertions. Three paired route runs retained HTTP 200 and identical 19,341-byte response hashes; old/current per-pair medians were 0.427339/0.393169, 0.422330/0.345271, and 0.365710/0.347531 ms/request, giving median-of-pair medians 0.422330/0.347531 (1.21x). The full coverage gate passes 285 files / 25,324 tests at 100.0% for all four lib/ metrics (51,685 detail rows, no stale annotations). The all-subroutine 50x goal remains open.

Problem 48 navigation item array removal (2026-10-18): expected output is byte-identical saved-before-skill markup without a temporary item array and final join. A red-first Docker test failed because the optimized wrapper contract was absent. The implementation appends ordered fragments to one scalar and wraps inline. The focused t/03-web-app.t suite passes 542 assertions. A 10-million-call Docker comparison measured 4.76 million array/join assemblies per second and 8.00 million direct-scalar assemblies per second (1.68x), with exact output parity. The warmed route retained HTTP 200, 19,341 response bytes, and the established hash at 0.342350 ms/request; this is within route variation and no end-to-end gain is claimed. The full coverage gate passes 285 files / 25,325 tests at 100.0% for all four lib/ metrics (51,685 detail rows; no stale annotations). Problem 48 remains in progress.
