4.29 2026-08-18 - DD-585/589/590/591/592/593: six query functions across CollectorRunner.pm, IndicatorStore.pm, RuntimeManager.pm, ActionRunner.pm, SkillManager.pm (three functions) and PageRuntime.pm each shelled out (via system(), Capture::Tiny, or a waitpid reap) and left Perl's global $? holding their own last subprocess's exit status after returning, without ever localizing $? at sub entry. A caller reading bare $? afterward for an unrelated reason - the shape that first surfaced this in DD-585, an END block in a test file - silently inherits the wrong value. Found by a systematic sweep of lib/ once the first instance was fixed, and independently by the autonomous hourly bug-hunt round, which found and fixed DD-590 and DD-593 on its own (both resumed here after the round's own session ended mid-verification, with nothing lost either time - its park mechanism correctly left the tree untouched while a suite was live). Fixed uniformly with `local $?;` at each sub's entry. One subtlety verified rather than assumed: SkillManager's three functions never read $? explicitly, but Capture::Tiny returns system()'s own return value automatically while system() ALSO mutates the global $? as an unavoidable side effect - confirmed with a standalone repro before writing any fix. A fourth possible instance (ActionRunner's own sibling query pattern) was checked and is genuinely absent. - DD-594: dd-round-park.sh's automatic end-of-round park comment defaults to the shared, retired 'DD Bot' identity (DD-527 retired it project-wide) because nothing in the automation chain ever sets DD_TIRA_PARK_AUTHOR. Found by the autonomous round tracing the root cause of a finding that had been recurring on every card it touched. - DD-586: t-dd-round-park.sh's specs-guard timeout (600s) was too tight for a spec that deliberately spawns real long-lived background processes to model a genuinely-alive concurrent round; raised to 1200s. - DD-587: the hourly bug-hunt and 10-minute blocked-resolver systemd timers had been silently disabled for 9 days. Re-enabled and verified both correctly defer to an active interactive session before doing real work. - DD-562: master's coverage gate read 100.0 locally but 99.9 on CI. - DD-567: a perl-interpreter CVE advisory blocked the CI step immediately before tests and coverage, on 5 consecutive pushes. - DD-568: an early CI step failing silently skipped the test and coverage steps that followed it; those steps now run with if: always(). - DD-572: the pre-push security grep matched 'brewfile' against the retired-system marker pattern 'ewf', making a required gate return permanent noise. - DD-573: 13 operator-tool specs existed under dd-tg with nothing ever running them, so a broken spec could never fail loudly. - DD-574: the audit gate could fail without ever saying which check failed it, a defect DD-567's own fix left behind. - DD-581: nothing recorded a coverage verdict anywhere the board's own gate indicator could read, so it could never show green even on a clean pass. - DD-582: gate-status reported RUNNING for a log file it had no way to know was actually still being written. - DD-583: the Telegram ack watcher answered a direct question about its own silence with more silence. - DD-584: two dd-tg operator specs had been failing with nobody told. 4.28 2026-08-15 - The coverage gate's own regression test no longer asserts that this machine's library tree exists everywhere. It runs the gate in an environment stripped of PERL5LIB and expected it to resolve its dependencies anyway, which is true only where those dependencies sit in the one directory the gate repairs. Where they do not, the gate correctly reports a missing module and the test called that a failure. It now skips those two cases with the reason printed, keeps every structural assertion, and still goes red if the repair is removed. - 4.27 was tagged but never published: this test failed in the release workflow, and a job that fails early makes every later step skip, so the tag exists with no release behind it. Nothing in 4.27 is lost - it is all here. 4.27 2026-08-15 - Browser smoke tests no longer hang a machine whose browser cannot reach its own loopback address. A coverage run sat on one such fetch for one day and twenty hours, holding a web server, a starman master and worker and a whole Chrome tree, while producing no failure, no exit status and no last line - a stalled run looks exactly like a slow one. Every external command those tests run is now bounded, and the bound kills the command's whole process group rather than the single process it can see, because the wedge left seven processes alive and killing the visible one would have reaped one of them. - Those tests now ask, before doing anything else, whether the browser can fetch a page that is definitely being served. If it cannot, they skip and print why - the probe URL, what the browser did, and that the product is not implicated. The probe serves that page over plain HTTP from a nine-line socket server rather than from the product, which is what makes it a diagnosis instead of a second copy of the test: if plain HTTP works and the real fetch then fails, that is a genuine failure and is not skipped. - The workspace command hands the terminal to tmux with exec instead of capturing the attach. It used to funnel a full-screen interactive program through a capture buffer nobody reads until it exits, and leave a perl process parked as its parent for the life of the session - possibly days. - Collectors: a lost pidfile no longer multiplies supervisors. Start, stop and status all treated that file as the authority on what was running, so once it was gone every start forked another loop that nothing could see, stop or count; twenty-seven were alive at once for a collector declared singleton, each firing every fifteen minutes. They now ask the process table, adopt the loop already running, and repair the record. - A collector stopped by a signal now records which signal stopped it and which process received it. A 'stopped' state with no pidfile is otherwise identical whether the loop shut down in an orderly way, was stopped by a watchdog, or was killed by something else on the machine - and that ambiguity sent four separate investigations down the wrong path. - The coverage gate carries its own library path. Launched from cron or a transient systemd unit - environments that read no shell profile - it died naming a dependency that is declared in three manifests and installed. It resolves that tree itself now, and still fails loudly when a dependency is genuinely absent, because it adds a directory only when the directory exists and runs no external command. - The collector start path no longer re-checks a pid it has already guaranteed. When the recorded pid is missing or empty it is replaced by a search of the process table, which returns a real pid or nothing, so the later test for a usable pid could never fail. The guarantee is now stated once where it holds instead of being re-checked where it cannot, which is one fewer piece of code that looks like it protects something. 4.26 2026-08-10 - Packaging: stopped the release tarball carrying two things it never should have. logs/ shipped the board sweep's own runtime output - findings about this project's cards - to anyone who installed the product, because dzil gathers from disk and cron recreates that directory hourly. SECURITY_CHECKS.md shipped the internal security-audit protocol, which belongs with the operator files that are deliberately never released. Both are excluded now and t/15 fails if either exclusion is removed. - Board tooling: the FT99 sweep decides from the cards themselves instead of a retired markdown page, so a card parked on the owner with nothing left to answer is reported rather than silently accepted, and a card holding an unanswered question anywhere else is reported too. It also reports a card claimed in a working column that has stopped moving, and one left untouched in the backlog. - Coverage gate: refuses to grade a database written by a different Devel::Cover install, and says which two disagree, instead of exiting with an unexplained status. 4.25 2026-08-09 - CI: restored a pipeline that had certified nothing for ten days. Three GitHub Action pins sat on the node20 line while their version comments claimed node24, so every run died at Setup Perl and the suite, the coverage gate and both CVE audits never executed (DD-449). The pin comments are now resolved against the upstream tag rather than trusted (DD-449), the environment export no longer swallows its own failure inside a command substitution (DD-483), and two suite files no longer assume tools and libraries CI installs elsewhere (DD-485). - Security: helper session ids are drawn from the operating system CSPRNG instead of a SHA over the pid, the wall-clock second and built-in rand - the construction CVE-2026-13577 describes in Dancer2, reimplemented here in the code guarding the web interface. Crypt::URandom is now a declared dependency floored at 0.55, because CVE-2026-2474 affects earlier releases (DD-452). - Security: dispositioned CVE-2026-13577 for Dancer2, which has no fixed release, behind a guard that fails the audit if any production code ever activates a Dancer2 session (DD-451). - Security: closed a Zip Slip in dashboard open-file, where a sources jar could write attacker-controlled paths anywhere the user can write (DD-498). - Security: corrected the CVE gate's subject so it judges the product's declared chain rather than the host interpreter (DD-499). - Fix: a collector's loop state is written before its pidfile, so a concurrent running_loops can no longer delete the pidfile of a healthy loop and leave it unstoppable by name while the supervisor forks a duplicate (DD-488). - Fix: Housekeeper's temp-file cleanup lists its directory instead of globbing a pattern built from the path, so a directory whose path contains a space stops hiding every real temp file and unlinking unrelated ones (DD-502). - Test reliability: the coverage gate, the empty-environ probe and several collector lifecycle fixtures no longer fail purely because the host is CPU-starved, which is the shape of every CI runner (DD-482, DD-486, DD-489). Every timeout changed here is backed by a measurement rather than by the fact that a larger number passes. 4.24 2026-08-08 - Security: closed the open-redirect class in the login redirect sanitizer and on both HTTPS-enforcement Host-header redirect paths (DD-415, DD-419), rather than only the single reported byte. - Security: closed three path-traversal classes - saved-page writes outside the dashboards root (DD-395), parent-directory traversal in every skill-namespaced web route with static-file containment asserted (DD-416), and skill installs whose repo name escapes the skills root (DD-426). - Security: every page-derived value the web layer places inside an HTML attribute is now escaped for attribute context (DD-421), and saved bookmark link segments are percent-encoded for URL context (DD-423). - Security: state-changing requests are rejected when Origin or Referer is foreign, on every web tier (DD-422). - Security: the transient-token denial now depends on the token alone, so a file value riding along can no longer bypass the 403 (DD-425). - Security: release tarballs no longer carry dogfood QA evidence, ticket worktrees (DD-414), or the Hermes runtime state root and its credentials (DD-432). - Feature: first-class self-upgrade - dashboard upgrade and d2 upgrade run the canonical platform installer (DD-386). - Feature: a real /favicon.ico is served instead of a 404 on every page load (DD-407). - Collectors: a timed-out command's full process subtree is terminated (DD-388); blocked Windows command collectors are interrupted at their timeout (DD-389); the atomic writer uses a per-process pending name so concurrent workers stop clobbering each other (DD-418); log append and rotation share a per-log lock (DD-399) and rotation cuts on entry boundaries so a combined lines+days rule cannot orphan an entry body (DD-427); collectors keep running when the updates directory is absent (DD-398). - Runtime: saved-Ajax disconnect cleanup terminates descendant processes (DD-396) and gives them a real elapsed SIGTERM grace window (DD-428); runtime state readers survive a transient empty state file (DD-411) and the web lifecycle no longer acts on incomplete runtime state (DD-412). - Windows: the SSL certificate home resolves through PathRegistry so the HTTPS server starts (DD-417); every passwd lookup is guarded so non-interactive sessions resolve a user and a home (DD-413); PERL5LIB generation is cross-platform (DD-397); PowerShell startup caches are anchored to the home layer and no longer clobber live PATH state (DD-390). - Web UI: every generated HTML document carries a lang attribute (DD-405); logout and an authorized /login route by trust tier (DD-408); page-editor textareas carry programmatic accessible names (DD-409); rendered page chrome no longer overflows 320px phone viewports (DD-410). - Performance: PathRegistry working-directory lookups no longer fork /bin/pwd repeatedly (DD-393). - Gates: the POD syntax gate reads the source tree instead of a stale copy and the three reds it was hiding are fixed (DD-429); the coverage gate grades lib/ instead of the stale build tree (DD-431) and a failed exec in the harness process can no longer blind it (DD-428); the Windows collector-timeout E2E is proven on a real QEMU Windows guest and made repeatable (DD-404). - Metadata: MakeMaker license metadata aligned with MIT (DD-400). 4.23 2026-07-29 - Coverage: every module under lib/ now reaches 100.0 on all four Devel::Cover metrics - statement, subroutine, branch, AND condition - driven by the new per-module hermetic coverage suites (t/59 through t/106) with genuinely-unreachable outcomes annotated inline and dead guard operands removed where redundancy caused unreachable branches. - CI: all three workflows (test, release-github, release-cpan) enforce the all-metric standard through script/check-all-metric-coverage, which fails closed on any metric below 100.0 or any malformed report (t/107-all-metric-coverage-gate.t). - Security: helper host-trust no longer accepts arbitrary Host headers that merely resolve to loopback, closing a DNS-rebinding vector; exact loopback literals and configured alias hosts remain trusted. - Test isolation: the fresh-runtime API assertions in t/15-cli-module-coverage.t now chdir into their isolated HOME so a dev-runtime layer in the working checkout cannot capture their writes. - Installability: the dependency hardening initially raised the Perl floor to 5.44, which cpanm rejects on every mainstream perl (perl:latest is 5.42); the floor is restored to 5.38 (the code's real requirement) while CI continues to run on Perl 5.44 and the dependency version pins keep the security gate. 4.22 2026-07-15 - the housekeeper now sweeps expired helper-session files each run (wiring the SessionStore expired-session sweep added in 4.21 into the periodic cleanup), so stale session records no longer accumulate unbounded between logins 4.21 2026-07-15 - batch of fixes from the automated bug-hunt (round 1), each test-covered: - config: the merged config now always dedupes the `collectors` array by name and `providers` by id, even when only one layer defines them (a single layer repeating a collector name previously leaked duplicates); config.json / api.json are now written atomically (temp file + checked close + rename) instead of truncated in place - security: the encoded-action transient path no longer trusts a payload-supplied `source`/trusted flag, closing a forged-token arbitrary command execution; the session id from the cookie is charset-sanitized before path use, closing a session-file path traversal; the session cookie gains `Secure` over HTTPS; helper passwords are now stretched with PBKDF2-HMAC-SHA256 while still verifying existing hashes - collectors: `timeout_ms` now round-trips correctly (no more x1000 blow-up on Windows worker restart); a `* * * * *` cron collector no longer bypasses per-minute dedup; signal-killed collector/action commands are recorded as failures instead of exit 0; background actions guard setsid on Windows - state writes: Collector and IndicatorStore atomic-write helpers now check close() and no longer unlink before rename; a git error is no longer reported as a dirty tree - added SessionStore::sweep_expired for expired-session reclaim (wiring it into the housekeeper, plus the DNS-rebinding host-trust tightening and the timed-out-command subtree kill, are deferred as follow-ups) 4.20 2026-07-15 - security: in `dashboard serve --ssl` the public front-proxy relays raw TLS to the internal loopback backend, so every backend connection arrived from 127.0.0.1; combined with a `Host: 127.0.0.1` header a remote HTTPS client was classified as trusted loopback admin and got unauthenticated admin. The internal SSL backend now marks itself proxied and never grants the loopback-admin shortcut, so remote HTTPS clients must authenticate. 4.19 2026-07-15 - ship `d2` as a real, installed command instead of only a shell-bootstrap alias: `bin/d2` is a first-class entrypoint that re-execs its sibling `dashboard` with the same arguments, so `d2 ` behaves exactly like `dashboard ` and now works in scripts, cron jobs, and fresh shells that never sourced the dashboard shell integration - the shell bootstrap keeps completion wiring for `d2`, and the generated shell shortcut remains as a convenience where the bootstrap is loaded 4.18 2026-07-15 - add `dashboard ask `: a uniform AI-assistant command that answers from the Anthropic API when an `ANTHROPIC_API_KEY` (or the `claude` config domain) is set, and otherwise falls back to the local `claude` CLI - support pluggable backends `--claude` (default), `--codex`, `--copilot`, and `--gemini`; the chosen backend becomes sticky per workspace, and each shells out non-interactively in a forced read-only mode - remember the conversation per workspace (keyed by WORKSPACE_REF or the active project root) so follow-up questions carry prior turns; `--new` starts fresh and `--no-memory` skips the transcript for one turn - inline `--file` text attachments into the prompt and pass image attachments natively (Anthropic image blocks, codex `-i`, copilot `--attachment`); the transcript is stored owner-only under the runtime state root 4.17 2026-07-15 - make `dashboard collector stop ` actually stop the collector: it now routes through the supervisor-aware stop path so the collector watchdog is deregistered and cannot immediately respawn the loop (previously it called the low-level loop stop directly, so the supervisor restarted the collector and its status snapped back to running) - stop the collector supervisor from creating a DUPLICATE loop when it races a freshly started loop before that loop has set its process title, by recognizing an already-running loop from its recorded state as well as by process identity - reset the consumer-facing collector `status.json` (running / active_runs) when a loop is stopped, so prompt, web, and CLI status readers reflect the stop immediately - reliably terminate long-running collector command subtrees on stop: send the worker process-group SIGKILL unconditionally so a SIGTERM-ignoring child is still reaped, kill the loop first and scale the TERM grace to the worker count so the loop is not KILLed mid-cleanup, persist active worker pids on spawn, and sweep orphaned workers left behind by a crashed loop - recognize managed collector loops from recorded state on hosts without `/proc` or `ps` (Windows), so stop can still terminate them 4.16 2026-06-11 - teach the built-in `dashboard workspace` command a `-c` flag, accepted before or after the workspace name, that changes into the registered dashboard path for that name before planning the session, so `dashboard workspace -c foobar` behaves like `cdr foobar` followed by `dashboard workspace foobar` and the tmux session plus its layered `.env` refresh start from the registered project directory - fail `-c` explicitly when the workspace name is not a registered dashboard path, when the registered target is not a directory, or when the directory cannot be entered, instead of silently starting the workspace from the wrong directory 4.15 2026-06-11 - keep the `install.sh` post-install activation runner on the same shell dialect as the bootstrap target, so blank hosts without `SHELL` exported (fresh Docker containers, `curl ... | sh` runs) no longer source the bash-flavoured activation files through plain `sh` and die on the first bash-only construct 4.14 2026-06-11 - align every module under `lib/` back onto the single repository release version after the `4.13` bump only advanced `Developer::Dashboard`, leaving the other 53 packaged modules still declaring `4.12` - add a release-metadata gate that walks every `lib/**/*.pm` and asserts its `$VERSION` matches the `dist.ini` release version, so a partial version bump can never ship again - validate IPv4 loopback octets strictly in `Auth::_ip_is_loopback`, so malformed literals such as `127.0.0.999` are no longer classified as loopback addresses - restore the `4.12` entries in `Changes` and `FIXED_BUGS.md` that the `4.13` update overwrote instead of prepending 4.13 2026-06-11 - harden the GitHub-hosted `Devel::Cover` gate again by exercising the installed-dist `File::ShareDir` asset lookup branch in `Web::App::_bundled_public_asset_path`, so clean Ubuntu runners close the final `99.9` statement gap and keep the reviewed `lib/` totals at `100.0 / 100.0 / 100.0` 4.12 2026-06-10 - harden the GitHub-hosted `Devel::Cover` gate by exercising the low-traffic `Web::App` compatibility helpers directly in the focused web coverage tests, so route fan-out timing on CI runners cannot leave the reviewed `lib/` totals below `100.0 / 100.0 / 100.0` 4.11 2026-06-10 - decouple the GitHub-hosted CPAN upload workflow from ordinary `vX.XX` tag pushes so signed GitHub release tags can be published without triggering an unasked PAUSE upload - broaden the CodeQL push trigger to every branch so repository-side SAST coverage is visible on more commit paths instead of only `master` 4.10 2026-06-10 - move project-local isolated Docker Compose service discovery and disable markers under `./.developer-dashboard/config/docker/...` so the implementation matches the documented config-root contract across resolver lookup, toggle writes, and exported `DDDC` paths 4.09 2026-06-06 - keep smart-routed skill page browser edits on their canonical `/app/` and nested `/app//` aliases by teaching the edit/source routes and Play handoff to load skill index pages through the same smart resolver instead of collapsing to the underlying `BOOKMARK: index` id 4.08 2026-06-06 - split the Web UI bookmark editor into Jupyter-like section blocks while keeping the saved bookmark file format canonical, including block-aware syntax overlays, Tab-created sections, hidden source recomposition, and Play-mode save-and-render handoff - standardize operator-facing built-in CLI output so path, file, api, and skill inventory or mutation commands default to readable table summaries while `-o json` returns the full raw machine payload 4.04 2026-06-05 - restore the public switchboard helper path contract for dotted `dashboard .` dispatch, keep Windows core-backed built-ins routed through the shared staged `_dashboard-core` helper, and fix the staged private core import drift that broke `dashboard init` and later helper refreshes - harden Windows runtime helper and state-file flows so detached web, collector-loop, collector-worker, and watchdog launches use the staged foreground helper entrypoints and tolerate Windows rename collisions through explicit replace fallbacks - teach the Windows checkout bootstrap to seed `HOME` from PowerShell's own `HOME`, create a stable user-space `make.cmd` shim for Strawberry GNU make, and keep the fuzz gate lightweight under `Devel::Cover` - force non-interactive CPAN environment defaults for skill `cpanm` installs so Windows fresh-session bootstrap checks do not stall behind dependency configure prompts 4.03 2026-06-05 - ship an explicit OWASP compliance SOW and evidence matrix, wire it into the OWASP and release-metadata tests, and tighten the manuals so the safe public wording stays `OWASP-aligned` / `OWASP-gated` until the remaining governance blockers are truly closed 4.02 2026-06-04 - fix `_helper_file_supports_internal_command` substring detection so runtime helper refresh probes return true for matching staged helpers and stay stable under the plain and covered harnesses - harden `t/47-zombie-coverage-closure.t` with a persistent helper fixture and direct runtime-object assertions so the final gate loop stays deterministic 4.01 2026-06-02 - add the built-in `dashboard api` management command for layered `config/api.json` machine auth, including merged registry listing, per-key inspection, SHA-256 hashing of raw secrets before persistence, optional `--maybe-secret` route-oriented secret input, exact saved `/ajax/...` route add/remove controls, duplicate-route no-op handling, and child-layer tombstones that hide inherited API groups without rewriting parent config 4.00 2026-06-02 - add layered `config/api.json` machine auth for selected saved `/ajax/...` routes, including installed skill `config/api.json` fragments, exact route allowlists, and `X-DD-API-Key` plus `X-DD-API-Secret` header verification against stored SHA-256 digests - keep helper-session auth working on those same API-registered ajax routes while failing unauthenticated or wrong-secret machine callers closed with `403 {"status":"forbidden"}` - pass saved ajax API auth headers through the PSGI/Dancer adapter so browser-facing and direct HTTP entrypoints enforce the same machine-auth contract 3.99 2026-05-26 - keep the active shell directory in dashboard-managed child `PATH` repairs alongside the current Perl interpreter bin so collector shell commands still execute cleanly during blank-environment tarball installs and any runtime that inherits a stripped `PATH` 3.98 2026-05-20 - treat collector `disable` as a hard stop-and-skip flag so disabled collectors are not started, explicit named starts reject them, and any already-running managed loop is stopped during the next lifecycle action - remove managed collector indicators for disabled collectors instead of leaving stale active indicator state behind - normalize dashboard-owned `PERL5LIB` ordering so core/site/vendor Perl dirs stay ahead of inherited user-local shadow copies, preventing stale dual-life XS modules such as `Encode` from breaking helper startup, collector child commands, saved Ajax subprocesses, and skill hooks on hosts with older local-lib artefacts - keep the current Perl interpreter directory at the front of `PATH` for dashboard-managed child commands so collector loops, saved Ajax subprocesses, and skill hooks do not fall back to a mismatched system `perl` when they execute `dashboard` through `/usr/bin/env perl` - run collector shell commands through a non-login shell so macOS shell session restore output and other shell-init chatter do not get prefixed onto JSON collector output - slow tmux ticket/workspace status refresh from a 2-second hot loop to a 15-second cadence so dashboard-managed tmux sessions do not burn CPU just to repaint status fragments - apply a 30-second default safety floor to shell collectors that recurse back into `dashboard` or `d2`, with explicit opt-outs and an environment override for intentionally fast dashboard polling - pause the collector watchdog supervisor during explicit named collector stop and restart actions, then restore supervision for the remaining watched fleet afterwards so watchdog restarts do not race manual lifecycle commands - preserve the saved managed web pid during stop flows even when shutdown has to fall back to persisted-state listener pids, and still escalate the saved pid to KILL when process discovery lags after TERM - scope web stop ajax cleanup to the current dashboard runtime root instead of sweeping every `dashboard ajax:` process owned by the user - make collector loop shutdown kill active worker process groups so long-running singleton commands stop with the collector loop instead of surviving as orphaned subprocesses - reap exited collector workers and watchdog-adopted dashboard children immediately on `SIGCHLD` instead of waiting for the next scheduler or watchdog tick, so long-interval collectors and orphaned supervisors do not leave visible `` dashboard processes behind for minutes or days - persist active worker pids in collector loop state and lock in a live singleton regression where a long-running due run must be skipped instead of overlapped 3.92 2026-05-20 - keep `dashboard ps1` on a lighter switchboard path by lazy-loading the unknown-command suggestion runtime and helper-staging-only modules instead of paying for them on every prompt render - stage only the requested built-in helper on the hot path and reuse one `PathRegistry` object across the switchboard invocation, instead of rescanning and restaging the entire helper tree for every prompt - load installed skill config fragments directly from layered `config/config.json` files in `Config` so prompt-time config reads do not pull in the full skill dispatch runtime 3.91 2026-05-20 - make `dashboard ps1` skip `tmux show-environment` when `TMUX` is unset, stop constructing an unused collector object, and refresh only prompt-visible core indicators on the prompt path - read the prompt branch directly from `.git/HEAD` and worktree `gitdir:` metadata instead of spawning `git branch`, keeping branch rendering fast on slower hosts such as iSH - add a collector-sync fast path so repeated prompt renders avoid the full collector indicator rewrite path when the stored config-backed indicator state already matches 3.90 2026-05-20 - make `PathRegistry` actually reuse a precomputed invocation cwd and memoize repeated DD-OOP-LAYERS path derivation so thin helper commands stop recalculating the same runtime chain during one process - make `EnvLoader` plain-directory traversal reuse the registry cwd cache instead of re-running `cwd()` while it walks root -> project -> leaf `.env` layers - tighten the nested skill env/compose fix set with the final blank-host and coverage gate closure work 3.89 2026-05-19 - expand nested skill env loading from root to leaf for dotted skill commands and participating docker compose skill services, preserving overwritten parent values under cumulative aliases such as `foo_VERSION` and `foo_bar_VERSION` - discover nested installed-skill docker compose roots so participating leaf services such as `skills/foo/skills/bar/skills/zzz/config/docker` contribute both leaf `zzz_DDDC` and cumulative `foo_bar_zzz_DDDC` aliases during compose resolution - cover the remaining action-runner defensive and background-child startup branches so the final tree can close the 100% coverage gate again 3.88 2026-05-19 - load participating skill-root `.env` files into `dashboard docker compose` resolution and export normalized `_DDDC` variables for each participating skill docker root - run detached background page command actions through a reaped supervisor path so blank-container tarball installs and root-owned hosts do not leave stale background wrappers behind or fail the tarball gate 3.86 2026-05-19 - fixed packaged shell bootstrap generation so staged helpers always re-enter the active dashboard entrypoint instead of leaking a stale inherited source-tree path into extracted tarball installs and tests 3.85 2026-05-19 - export one skill-specific _DDDC compose env variable for each participating skill docker root - keep participating skill .env loading isolated to docker compose resolution without executing skill .env.pl files 3.84 2026-05-19 - load participating installed skill .env files into docker compose resolution without executing skill .env.pl files 3.83 2026-05-18 - fixed interactive `dashboard skills install` progress so dependency rows are only added when the matching manifest file actually exists in the fetched skill, instead of printing `[OK] ... skipped: ... not present` noise for absent files - filtered operating-system-specific dependency rows so `aptfile`, `apkfile`, `dnfile`, `wingetfile`, and `brewfile` only appear on their matching host families and stay hidden on unrelated platforms even when those files exist in the skill - kept cross-platform manifest rows such as `package.json`, `requirements.txt`, `cpanfile`, `cpanfile.local`, `Makefile`, `ddfile`, and `ddfile.local` visible only when those files are present, which leaves the progress board focused on real work instead of skipped probes 3.79 2026-05-16 - replaced the `tomq` backend dependency with `TOML::Parser`, inflating TOML booleans to plain Perl `1` and `0` scalars so query output stays stable while the blank-container `cpanm` install gate no longer trips over the failing upstream `TOML::Tiny 0.21` test suite on Perl 5.38 3.78 2026-05-16 - fixed the tag-driven GitHub release workflow so it installs `Devel::Cover` before running the numeric coverage gate; this avoids the exit-127 `cover` failure that previously blocked signed GitHub release publication and kept Scorecard from seeing any releases 3.77 2026-05-15 - aligned every shipped Perl module under `lib/` with the distribution version after the blank-host installer fix so the source tree, release metadata, and CI coverage gates all read one consistent `3.77` version instead of a mixed `3.75`/`3.76` module set 3.76 2026-05-15 - fixed blank Ubuntu `curl ... | sh` bootstrap installs so `install.sh` seeds `File::ShareDir::Install` into `~/perl5` before `cpanm --notest .` runs against the cloned checkout, avoiding the configure-time `Can't locate File/ShareDir/Install.pm` failure on fresh hosts - aligned `install.ps1` with the same configure-prerequisite bootstrap so the checkout install path does not drift between Unix-like and Windows operators 3.75 2026-05-15 - fixed Windows background web helper lookup so source-tree and CI test runs no longer die when `RuntimeManager` asks `File::ShareDir` for an installed dist share that does not exist yet; helper fallback now reuses the same guarded helper-asset resolver used elsewhere in the runtime - replaced the built-in `/js/jquery.js` shim with a bundled local copy of jQuery 4.0.0, while keeping `/js/jquery-4.0.0.min.js` as a compatibility alias for the same shipped asset - taught the browser bookmark editor to expand `:---` into the full separator line and insert the next sensible unique directive so common `TITLE -> HTML -> CODE` authoring flows are smoother - added release and route coverage for the bundled jQuery asset and the missing-dist-share helper fallback path 3.74 2026-05-15 - added direct `python` dispatch for executable `cli/*.py` commands and hook files so Python-backed skills now run through the same logical command and hook resolution path as Perl, Node, Go, Java, and shell helpers - added `requirements.txt` to the skill dependency chain after `package.json`, installing Python dependencies through `python -m pip install --user --requirement requirements.txt` - regenerated README.md from the canonical POD after the runtime and dependency-chain changes so release metadata stays in sync 3.73 2026-05-15 - added direct `node` dispatch for executable `cli/*.js` commands and hook files, so JavaScript-backed skill commands now run through Node in the same extension-resolution path already used for Perl, Go, Java, and shell helpers - fixed the platform runnable-file resolver so logical command names can discover matching `.js` files on Unix and Windows without breaking the existing `package.json` dependency-install chain used by `dashboard skills install` - fixed the prompt tmux-status unit coverage to clear `WORKSPACE_REF` explicitly so ordinary tmux sessions still test the non-dashboard path 3.72 2026-05-14 - fixed collector watchdog supervision so a managed loop that stays alive but stops making progress is treated as stalled, restarted automatically, and reported explicitly instead of sitting silent until an operator notices - renamed the primary tmux workflow from `dashboard ticket` to `dashboard workspace`, while keeping the older `dashboard ticket` spelling plus `TICKET_REF` session state as compatibility fallbacks - added layered plain-directory `.env` refresh for workspace sessions, so the highest ancestor `.env` acts as the base, parent `.env` files merge next, the current directory `.env` overrides last, and resumed tmux workspace sessions are refreshed in place with dropped keys unset 3.71 2026-05-14 - fixed live collector status writes so managed indicator ordering now keeps the collector array order from `config/config.json` even after one collector refreshes its own state, instead of dropping back to alphabetical order in `/system/status`, page-header indicators, and `dashboard ps1` - fixed runtime `config/routes.json` aliases so normal saved bookmarks such as `/app/learn.ai` can be reached through shorter custom paths like `/java`, and widened that runtime alias loading across `/app`, `/ajax`, `/js`, `/css`, and `/others` instead of limiting custom paths to installed skill route metadata only 3.70 2026-05-13 - moved skill route metadata to `config/routes.json` and changed the schema to the flat custom-path form, where each public path maps to one local smart route string or an object with `to` and optional `type` - expanded custom skill route support beyond Ajax so installed skills can publish fallback custom paths for `/app`, `/ajax`, `/js`, `/css`, and `/others` without breaking the smart longest-prefix parent routing - kept saved skill-page Ajax URL generation on the canonical custom path from `config/routes.json`, with Ajax custom routes defaulting to `json` unless an explicit route-level `type` is supplied - fixed browser `/system/status`, page-header status, and `dashboard ps1` indicator ordering so managed collector indicators now follow the collector array order from config instead of drifting alphabetically - fixed `dashboard serve logs -f` so a line appended immediately after the initial tail output is no longer skipped by a seek-to-end race before follow mode starts streaming new content 3.69 2026-05-13 - added skill-local `dashboards/routes.json` metadata so installed skill Ajax handlers can publish canonical custom paths, optional fallback aliases, and default response types such as `json`, `html`, or raw mime strings without breaking the smart `/ajax//...` resolver - fixed saved skill-page Ajax URL generation so `Ajax(file => ...)` emits the declared canonical custom route when route metadata exists, while the longest-prefix smart `/ajax/...` route remains the primary resolver and custom aliases are fallback-only before a normal `404` - fixed browser `/system/status`, page-header status, and `dashboard ps1` indicator ordering so managed collector indicators now follow the collector array order from config instead of drifting alphabetically 3.68 2026-05-13 - added collector overlap control with explicit `mode` and `multiple` config fields, defaulting collectors to singleton scheduling and allowing bounded parallel runs only when a collector opts into `mode = multiple` - fixed collector loop scheduling so long-running jobs no longer block the next interval tick for opt-in multiple-mode collectors, while singleton collectors still suppress overlap by design - made collector status updates lock-safe so active-run counts and the `running` flag stay correct when overlapping collector workers finish concurrently 3.67 2026-05-13 - fixed managed runtime child cleanup so detached web startup helpers, collector stop paths, the collector watchdog supervisor, SSL frontend connection workers, and background page actions no longer leave zombie processes behind on hosts such as macOS and WSL - fixed the SSL frontend accept loop so child-reaping `SIGCHLD` traffic no longer makes the public listener shut its backend down early, while TERM/INT/HUP now request a clean frontend shutdown explicitly instead of depending on interrupted `accept()` side effects - tightened regression coverage so collector stop and watchdog shutdown tests fail if the runtime leaves direct child processes unreaped, and action/background SSL runtime tests now enforce the no-zombie contract 3.66 2026-05-08 - added a collector watchdog supervisor that restarts unexpectedly-dead managed collector loops after startup instead of leaving them silently stopped until an operator notices - persisted watchdog restart counters, timestamps, error text, and `attention_required` collector status metadata so repeated crash loops are explicit in `dashboard collector status ` and collector logs - stopped the watchdog from thrashing forever by raising an explicit attention-required state after too many crashes inside the watchdog window, forcing a visible operator follow-up instead of silent churn 3.64 2026-05-08 - fixed macOS Terminal.app shell bootstrap to source /etc/bashrc_Apple_Terminal when update_terminal_cwd is missing, preventing "command not found" errors on fresh macOS bash sessions 3.63 2026-05-08 - added dockerfile manifest support to skill installation, allowing skills to declare Docker images that get built automatically during install - fixed Progress.pm to support unlimited streaming output for long-running operations like Docker builds, with optional max_detail_lines cap - added has_dockerfile metadata to skill list and usage output - fixed macOS Terminal.app shell bootstrap to source /etc/bashrc_Apple_Terminal when update_terminal_cwd is missing, preventing "command not found" errors on fresh macOS bash sessions 3.62 2026-05-07 - remove matching root ddfile skill source entries when uninstalling a skill 3.61 2026-05-06 - switched the project license from Perl_5 to MIT across the repository metadata, shipped LICENSE text, README, canonical POD, and Scorecard guardrails so the published license state is explicit and consistent - removed the old Artistic sidecar license file and Perl dual-license wording so the repo no longer advertises conflicting licensing terms 3.60 2026-05-06 - documented the open-source license disclaimer more explicitly in the canonical POD and synced README, making it clear that the software is provided "as is", no warranty is given, and normal free-software use is covered by the project's baseline liability disclaimer 3.59 2026-05-06 - fixed shared skill-nav discovery so nested installed skill trees such as `skills/ho/skills/coverage/dashboards/nav/index.tt` now render on the nested skill route itself and also join the shared nav strip above normal saved pages such as `/app/index` - taught the skill dispatcher to recurse through nested installed `skills/` trees when collecting skill nav pages for the web UI, while preserving deterministic order and disabled-skill filtering - fixed layered skill nav route-id discovery so `dashboards/nav/*` recursively includes nested fragment paths instead of only direct files in the first `nav/` directory 3.58 2026-05-06 - stamped dashboard-managed helper bodies with an explicit `developer-dashboard-managed-helper-version` marker so installed hosts can prove which helper build they actually have when staged helper behavior drifts from the active tarball - fixed Debian-family bash bootstrap installs and repairs so dashboard-managed shell lines are kept above the standard non-interactive `return` guards in `~/.bashrc`, including the Ubuntu single-line `[ -z "$PS1" ] && return` form, which keeps `dashboard ticket` tmux status commands working on installed hosts such as `hp.local` - taught `dashboard doctor` to audit and repair that misplaced `~/.bashrc` bootstrap shape with `dashboard doctor --fix` - fixed Windows `.cmd` and `.bat` command dispatch on Linux, WSL, and packaged-install hosts so an extensionless local `cmd` helper found in `PATH` is normalized back to `cmd.exe` instead of being treated as a custom command processor - taught `dashboard doctor` to audit staged helper drift under `~/.developer-dashboard/cli/dd/`, report stale or missing dashboard-managed helpers such as `_dashboard-core`, and restage them with `dashboard doctor --fix` when the currently shipped helper assets can repair the runtime - added direct regression coverage for extensionless `cmd` shims, stale helper staging, and shell bootstrap drift through both source-tree and packaged-install paths - fixed packaged helper asset discovery so tarball, PAUSE, and `cpanm` install-test trees keep resolving `share/private-cli` from a stable absolute module source path even after later `chdir` calls in long-running test processes - hardened the post-build smart-router two-stage Docker guard so one transient upstream `cpanm` fetch or unpack failure is retried once before the repository packaging gate is treated as broken - fixed the installer docs so they match the real checkout/master bootstrap behavior and the current shell-bootstrap repair workflow 3.45 2026-05-05 - fixed Unix-like `install.sh` so blank streamed installs no longer fall back to the stale `Developer::Dashboard` CPAN target; when no explicit `DD_INSTALL_CPAN_TARGET` override is set they now install the current checkout directly or clone GitHub `master` into a temporary checkout for `curl ... | sh` style bootstraps - added `tmux` to the shipped `aptfile`, `apkfile`, `dnfile`, and `brewfile` bootstrap package sets because `dashboard ticket` is a first-party tmux workflow - fixed blank Ubuntu bootstrap coverage so Debian-family installer docs and tests now prove the extra XS build header requirement for the live `cpanm` dependency chain instead of silently assuming the base image already ships it 3.43 2026-05-05 - fixed managed helper staging so upgraded home runtimes remove dashboard-owned older flat helpers from `~/.developer-dashboard/cli/` and keep the active built-in helper surface converged on `~/.developer-dashboard/cli/dd/` - added direct staged-helper regression coverage so the managed `dashboard shell bash` helper itself must emit the ticket tmux status bootstrap after staging, not just the repo checkout command path - fixed the blank-environment integration image so host-built tarball installs now include the native CPAN build packages required by XML::Parser, Net::SSLeay, and related transitive prerequisites - switched the blank-environment integration image to a real Debian Chromium runtime so browser smoke checks no longer die on the Ubuntu snap-wrapper launcher stub - fixed the host-side blank-environment launcher so it rebuilds the integration image from the current Dockerfile before running the installed-runtime smoke, preventing stale cached images from hiding browser-runtime fixes 3.42 2026-05-05 - fixed blank-mac `curl ... | sh` bootstrap so `install.sh` now bootstraps Homebrew automatically before reading the repo `brewfile` and continuing the normal macOS package flow - fixed Linux runtime lifecycle isolation so `dashboard restart` and `dashboard stop` no longer adopt or kill Developer Dashboard web and collector pids that belong to a different pid namespace such as a sibling Docker container - added Docker-style rolling detail lines to `dashboard skills install` so long-running dependency manifests keep the epic task board visible while streaming the newest manifest output under the active step - fixed tmux ticket-session prompt/status behavior so existing and new `dashboard ticket` tmux sessions suppress inline prompt indicators, move the full indicator strip into the first row of a two-line bottom tmux status block, keep tmux's normal indexed session/window row beneath it, preserve TT-backed percentage indicators and live collector values, refresh automatically through tmux status updates, and leave ordinary tmux sessions on the normal inline prompt path 3.40 2026-05-05 - moved prompt indicators out of inline shell prompts and into tmux `status-right` whenever the generated shell bootstrap runs inside tmux, while keeping the existing prompt indicators outside tmux - added a dedicated `dashboard ps1 --mode tmux-status` formatter and a `--no-indicators` prompt path so bash, zsh, POSIX sh, and PowerShell all share the same tmux-aware indicator behavior 3.37 2026-05-02 - added an explicit InternalCLI coverage guard for installed layouts where File::ShareDir already returns the private-cli root, keeping the numeric Devel::Cover gate at 100 percent after the Windows bootstrap fixes - added a Windows smoke guard that re-enters a fresh profile-loaded PowerShell session after install.ps1 so the streamed bootstrap must resolve `dashboard`, print `dashboard version`, and run `dashboard logs` 3.36 2026-05-02 - fixed the generated PowerShell profile bootstrap so fresh Windows sessions no longer try to pass the multi-line `dashboard shell ps` output array straight into `Invoke-Expression` - fixed the streamed Windows installer so `irm .../install.ps1 | iex` now writes a self-contained profile block that can load `dashboard`, `local::lib`, and the PowerShell prompt bootstrap in future sessions 3.35 2026-05-02 - fixed the installed private helper asset lookup on Windows so `dashboard init` no longer stops at an empty `MSWin32-x64-multi-thread/auto/Developer/Dashboard/private-cli` directory when the real shipped helper assets live under `auto/share/dist/Developer-Dashboard/private-cli` - fixed the streamed Windows checkout bootstrap so a blank host can now finish `dashboard init` after `irm .../install.ps1 | iex`, stage the home helper runtime, and continue into the generated PowerShell shell bootstrap cleanly 3.34 2026-05-02 - fixed the installed private helper asset lookup so Windows local::lib installs no longer look for `_dashboard-core` under the wrong arch-auto path when the shared dist asset root actually lives under `auto/share/dist/Developer-Dashboard/private-cli` - fixed the streamed PowerShell bootstrap so `install.ps1` now runs `dashboard init` before activating `dashboard shell ps` in the current shell, ensuring the staged home helper runtime exists before the bootstrap asks for PowerShell shell wiring - fixed the generated PowerShell profile guard so future sessions only ask `dashboard shell ps` for bootstrap output after the staged home helper runtime exists under `~/.developer-dashboard/cli/dd/_dashboard-core` 3.33 2026-05-01 - fixed the packaged install metadata so blank Windows hosts no longer pull test-only dependencies such as `Plack::Test` and `Test::Pod` during `cpanm --notest Developer::Dashboard`, which previously dragged the `Test::SharedFork` chain into the streamed PowerShell bootstrap and broke fresh installs - fixed the streamed Windows checkout bootstrap so it now installs from the current GitHub checkout shape cleanly on Windows, including the Windows-safe `Makefile.PL` version path and the self-healing private helper staging path used by lightweight commands such as `dashboard encode` - fixed the PowerShell bootstrap so `install.ps1` now sets the CurrentUser execution policy to `RemoteSigned` before writing the generated profile, allowing new PowerShell sessions to load the `dashboard shell ps` bootstrap instead of failing with `running scripts is disabled` - fixed the generated PowerShell profile block so `PERL_MB_OPT` is now written with format-string quoting instead of nested literal quotes that broke new sessions with `Unexpected token '$ddInstallRoot""'` - replaced the repository PSGI test dependency on `Plack::Test` with a local harness under `t/lib` so the release metadata can stay focused on real runtime requirements while the web route coverage still stays intact - corrected the install bootstrap docs so the Windows examples use the `install.ps1` entrypoint and describe the lighter packaged install path 3.30 2026-05-01 - fixed the streamed Windows bootstrap so native `winget` command output is written to the host terminal instead of leaking into typed helper return values, which previously broke the Strawberry Perl path hand-off during `irm .../install.ps1 | iex` - fixed the Windows Perl bootstrap so `install.ps1` no longer tries to self-install `App::cpanminus` while the downloaded `cpanm` bootstrap script is still running, avoiding the Windows file-replacement failure that broke the `local::lib` setup step on blank hosts - documented the exact streamed PowerShell bootstrap command as `irm https://raw.githubusercontent.com/manif3station/developer-dashboard/refs/heads/master/install.ps1 | iex` and updated the Windows install docs to describe the standalone `cpanmin.us` plus `local::lib` bootstrap flow accurately 3.28 2026-05-01 - renamed the Windows checkout bootstrap entrypoint from `install.ps` to `install.ps1` so the shipped filename matches normal PowerShell script expectations while keeping the streamed `irm ... | iex` flow explicit - hardened the Windows bootstrap winget path so package installs are pinned to the community `winget` source and the installer performs one `winget source reset --force` plus source refresh retry before failing with a clear HRESULT-style exit code 3.27 2026-05-01 - added a repo-root `install.ps1` bootstrap entrypoint for Windows PowerShell hosts so blank-machine installs can bootstrap Git, Strawberry Perl, Node.js, user-space Perl tooling, and the dashboard runtime from a checkout or a streamed `irm ... | iex` flow - updated the install bootstrap docs, release metadata guards, and shipped tarball asset checks so the Windows bootstrap path is tracked alongside the existing Unix-like `install.sh` flow 3.26 2026-04-30 - changed the blank-container tarball installation gate to use `cpanm --notest` after the normal source-tree test and coverage gates have already passed, so packaged dependency resolution and installed runtime behavior stay verified without rerunning the full tarball test suite - updated the blank-environment integration runner, release docs, and integration guardrails so the post-build `t/44-smart-router-two-stage.t` check and the broader tarball integration flow both follow the same `cpanm --notest` policy 3.25 2026-04-29 - fixed the installed Dancer web stack so the public `/app`, `/ajax`, `/js`, `/css`, and `/others` routes now reuse the backend smart route dispatcher instead of bypassing skill-local namespace resolution - added PSGI coverage that proves top-level and nested skill-local app, ajax, js, css, and others routes all work through the real installed web route layer - added the post-build `t/44-smart-router-two-stage.t` guard and wired the blank-environment host integration launcher to run it immediately after `dzil build` so extracted-dashboard routing regressions are caught at the tarball stage instead of only in source-tree tests - fixed helper-login blank-editor rendering so a helper user who lands on the root editor path still sees helper chrome, including the logout link and helper username, during installed integration runs 3.24 2026-04-29 - added skill-local web namespaces for `dashboards/ajax/*` and `dashboards/public/{js,css,others}/*` so installed skills can serve saved Ajax handlers and public assets without copying them into the shared dashboard roots - added longest-prefix route resolution so `/app`, `/ajax`, `/js`, `/css`, and `/others` can distinguish skill prefixes from normal nested global paths and can also resolve nested child skills under `skills//...` - kept nested global saved-bookmark assets and saved Ajax files working even when their leading path segments match an installed skill name 3.23 2026-04-29 - fixed the README source-of-truth process so the checked-in README.md is regenerated from the canonical Developer::Dashboard POD instead of being hand-edited independently - added a checkout sync helper and release-metadata guard so README.md must exactly match the generated Markdown output from the canonical POD - fixed the JS fast-check wrapper so npm update-notifier chatter does not break the stderr-clean fuzz gate on newer npm releases 3.22 2026-04-29 - regenerated README.md as real Markdown instead of accidental POD content - added a release-metadata guard so README.md cannot silently regress back into POD format 3.21 2026-04-29 - removed the final stale API Dashboard POD example from the public dashboard entrypoint so the extracted API and SQL dashboards are now fully pruned from core code, docs, POD, tests, and shipped assets 3.20 2026-04-29 - fixed bare dashboard skills install so a first successful install from the home root ddfile is reported as installed even when the skill ships no .env VERSION metadata, instead of showing unknown with blank version markers - fixed named collector stop summaries so dashboard stop collector NAME still reports the stopped collector row when the managed loop is alive but its process title has not become observable yet 3.19 2026-04-29 - extracted optional browser workspaces out of the core distribution, leaving the core runtime seeded-page set and manuals focused on the features that still ship in Developer Dashboard itself - removed core seeded-page handling, runtime bootstrap wiring, release metadata assertions, and shipped browser tests for the extracted optional browser workspaces - moved the extracted page assets, dedicated tests, and supporting notes out of the core repository tree so the default core tarball no longer carries that optional feature set 3.19 2026-04-29 - fixed dashboard stop and restart so they follow the saved managed listener port back to the real serving pid when the live web process renames itself into the underlying starman master listener shape - fixed minimal Docker lifecycle control so container stop/restart runs no longer lose ownership of the active listener after startup