Developer-Dashboard

 view release on metacpan or  search on metacpan

doc/testing.md  view on Meta::CPAN

```bash
prove -lr t
```

The dotted installed-skill command regressions cover both `cli/<command>.py`
and `cli/<command>.js`. The JavaScript execution assertions require `node` on
`PATH`, so the release tarball gate keeps the path-resolution assertions
everywhere and skips only the `.js` execution step on minimal hosts that do not
ship Node.js.

Collector lifecycle regressions also cover two macOS-specific runtime rules:
Dashboard-owned Perl child processes must keep the current interpreter ahead of
the system Perl in `PATH`, and collector shell commands must avoid login-shell
startup chatter that can prepend text ahead of collector JSON output.
Those path regressions now also lock in the shell side of that contract:
dashboard-managed child commands must keep the active shell directory in
`PATH`, not just the Perl interpreter bin, because `_run_command` executes
shell collectors through `sh -c` and blank-environment install verification
will fail if `perl` is found but `sh` is not.

Run the fast saved-bookmark browser smoke check with:

```bash
integration/browser/run-bookmark-browser-smoke.pl
```

That host-side smoke runner creates an isolated temporary runtime, starts the
checkout-local dashboard, loads one saved bookmark page through headless
Chromium, and can assert page-source fragments, saved `/ajax/...` output, and
the final browser DOM. With no arguments it runs the built-in Ajax
`foo.bar` bookmark case.

For a real bookmark file, point it at the saved file and add the specific
browser assertions you care about:

```bash
integration/browser/run-bookmark-browser-smoke.pl \
  --bookmark-file ~/.developer-dashboard/dashboards/test \
  --expect-page-fragment "set_chain_value(foo,'bar','/ajax/foobar?type=text')" \
  --expect-ajax-path /ajax/foobar?type=text \
  --expect-ajax-body 123 \
  --expect-dom-fragment '<span class="display">123</span>'
```

For a skill page that declares `config/routes.json`, assert the canonical
custom ajax path rather than the default smart `/ajax/<repo-name>/...` path:

```bash
integration/browser/run-bookmark-browser-smoke.pl \
  --bookmark-file ~/.developer-dashboard/skills/example-skill/dashboards/index \
  --expect-page-fragment "set_chain_value(endpoints,'status','/v1/status')" \
  --expect-ajax-path /v1/status \
  --expect-ajax-body '{"status":"ok"}'
```

For long-running saved bookmark Ajax handlers that would otherwise survive a
browser refresh, prefer `Ajax(..., singleton => 'NAME', ...)`. The runtime will
rename the Perl worker to `dashboard ajax: NAME`, terminate the older matching
Perl stream before it starts the refreshed one, and also tear down matching
singleton workers during `dashboard stop`, `dashboard restart`, and browser
`pagehide` cleanup beacons. For browser streaming checks, use `stream_data()`
or `stream_value()` against a finite saved Ajax handler and assert the final
DOM after incremental chunks land.

## Coverage

Install Devel::Cover in a local Perl library and generate the coverage report:

```bash
cpanm --notest --local-lib-contained ./.perl5 Devel::Cover
export PERL5LIB="$PWD/.perl5/lib/perl5${PERL5LIB:+:$PERL5LIB}"
export PATH="$PWD/.perl5/bin:$PATH"
cover -delete
HARNESS_PERL_SWITCHES=-MDevel::Cover prove -lr t
PERL5OPT=-MDevel::Cover prove -lr t
cover -report text -select_re '^lib/' -coverage statement -coverage subroutine
```

Developer Dashboard expects a reviewed `lib/` coverage report before release, and the current repository target is 100% statement and subroutine coverage for `lib/`.
This is a standing QA gate for every change, not only releases. After the
normal `prove -lr t` test gate passes, run the numeric `Devel::Cover` gate and
do not treat the work as done until the `cover` summary still reports 100%
statement and 100% subroutine coverage for `lib/`.

The coverage-closure suite includes managed collector loop start/stop paths under `Devel::Cover`, including wrapped fork coverage in `t/14-coverage-closure-extra.t`, so the covered run stays green without breaking TAP from daemon-style child processe...
Managed collector children now scrub inherited `PERL5OPT` and `HARNESS_PERL_SWITCHES` coverage settings before their long-lived loop work begins, and the runtime manager widens its startup stability polls when the parent harness is running under `Dev...
The collector stop path is also part of that regression surface now: a managed
loop must be truly gone before its pid/state files are cleaned up, otherwise a
dying old loop can keep rewriting state while a replacement restart is trying
to prove its new pid. `t/07-core-units.t`, `t/09-runtime-manager.t`, and the
covered `t/05-cli-smoke.t` restart/serve flows now lock that race down.
The runtime child-lifecycle contract is also part of the regression surface now: collector stop paths, collector loop parents, watchdog shutdown, detached background actions, and the SSL frontend must reap the direct children they own so macOS, Linux...
Collector scheduler coverage now also locks in the overlap policy contract:
default collector mode is singleton, opt-in `mode => multiple` collectors can
overlap only up to their `multiple` bound, and concurrent worker completion
must keep `active_runs` plus `running` status accurate under lock.
The live singleton regression now also proves that a real long-running command
still holds the single active worker slot across multiple due ticks, and that
stopping the collector loop tears down that long-running command instead of
only stopping the loop wrapper.
Collector disable coverage now also locks in the stop-and-skip contract:
`disable => 1` collectors are not started, explicit named starts reject them,
already-running managed loops for those collectors are stopped on the next
lifecycle action, and managed collector indicators are removed instead of
staying behind as stale active state.
GitHub workflow coverage gates must match the `Devel::Cover` `Total` summary
line by regex rather than one fixed-width spacing layout, because runner or
module upgrades can change column padding without changing the real
`100.0 / 100.0 / 100.0` result.
The `t/07-core-units.t` collector loop guard treats both `HARNESS_PERL_SWITCHES` and `PERL5OPT` as valid `Devel::Cover` signals, because this machine uses both launch styles during verification.

Dashboard bootstrap now also treats inherited `PERL5LIB` ordering as part of
the runtime contract. The public switchboard, staged private helper core,
runtime child-process env builders, and skill command env builders must keep
the active interpreter's core, site, and vendor directories ahead of any
user-local shadow copies so stale dual-life XS modules such as `Encode` do not
break helper startup on macOS or other hosts with older local-lib artefacts.
The runtime-manager coverage cases also use bounded child reaping for stubborn process shutdown scenarios, so `Devel::Cover` runs do not stall indefinitely after the escalation path has already been exercised.
The collector indicator ordering regression also stays under direct unit
coverage now: a live `CollectorRunner->run_once()` status write must preserve
the existing managed indicator `collector_order`, otherwise `dashboard ps1`,

doc/testing.md  view on Meta::CPAN

- direct `.py` custom commands plus executable `.py` hook files resolving through `python`, and direct `.js` custom commands plus executable `.js` hook files resolving through `node`, without breaking existing `.pl`, `.go`, `.java`, `.sh`, `.bash`, `...
- directory-backed custom commands through `~/.developer-dashboard/cli/<command>/run`
- non-destructive home helper staging, with dashboard-managed helpers isolated under `~/.developer-dashboard/cli/dd/` and user commands plus hooks preserved under `~/.developer-dashboard/cli/`
- MD5-aware `dashboard init` helper and seed refreshes, including unchanged mtimes when a dashboard-managed helper or seeded starter page already matches the shipped content
- empty-object `config.json` bootstrapping when `dashboard init` or `dashboard config init` finds no existing config file, without seeding an example collector
- project-local `./.developer-dashboard` precedence over the home fallback for bookmarks, config, CLI commands and hooks, auth users, sessions, and isolated docker service folders
- when changing starter-page refresh logic, run `prove -lv t/04-update-manager.t` and `prove -lv t/05-cli-smoke.t` so the core seeded-page init/update refresh path stays covered against stale managed saved copies
- when changing bookmark rendering, verify both the browser route and the CLI render path with the same TT bookmark: run `prove -lv t/05-cli-smoke.t` for `dashboard page render <id>` coverage and `integration/browser/run-bookmark-browser-smoke.pl --b...
- when changing Template Toolkit rendering or `nav/*.tt`, verify syntax-error handling too: broken TT must surface a visible `runtime-error` block and must not leak raw `[% ... %]` source in either the browser route or `dashboard page render`
- when changing `dashboard serve --ssl`, run `prove -lv t/17-web-server-ssl.t` and `prove -lv t/33-web-server-ssl-browser.t` so both the certificate profile and the real Chromium browser path stay covered; the generated cert must keep SAN coverage fo...
- when changing layered machine auth for saved `/ajax/...` handlers, run `prove -lv t/07-core-units.t` and `prove -lv t/08-web-update-coverage.t` so `config/api.json` still follows `DD-OOP-LAYERS`, installed skill `config/api.json` fragments still co...
- when changing the operator-facing `dashboard api` workflow, also run `prove -lv t/05-cli-smoke.t` and `prove -lv t/15-cli-module-coverage.t` so the built-in helper still lists the effective merged registry, hashes raw secrets from `--secret` or `--...
- when changing runtime-local optional Perl dependency handling, run `prove -lv t/05-cli-smoke.t` and `prove -lv t/28-runtime-cpan-env.t` to verify `dashboard cpan DBD::Driver` still installs into `./.developer-dashboard/local`, appends the runtime `...
- Docker Compose file, project, service, addon, mode, and env resolution
- bookmark syntax parsing, placeholder rendering, `TITLE` head-only rendering, and sandpit-isolated `CODE*` execution

The repository also now enforces:

- function-level purpose/input/output comments across the Perl codebase
- POD trailers under `__END__` for modules, scripts, update scripts, and tests
- `FULL-POD-DOC` sections in every repo-owned Perl file, covering purpose, why
  the file exists, when to use it, how to use it, what uses it, and multiple
  concrete examples that include the common path plus at least one meaningful
  edge or debugging path when the file owns one, with
  `t/15-release-metadata.t` acting as the release gate for that documentation
  floor
- a full-manual audit whenever `Developer::Dashboard.pm` changes, including
  FAQ wording, `SEE ALSO` target validation, and rejection of brittle
  `L<Developer::Dashboard::...>` private-module links in
  `t/15-release-metadata.t`
- rejection of repo-internal `*.md` filename references in the synced
  top-level product manuals and in shipped Perl POD, enforced through
  `t/15-release-metadata.t`
- explicit setup for env-sensitive tests, so checks that depend on blank
  variables such as `RESULT` clear or localize that state instead of assuming
  the parent shell or packaging harness starts empty

The web tests also cover the access model:

- exact `127.0.0.1` admin bypass
- `localhost` helper login requirement
- helper login session creation and logout
- helper session remote-address binding and expiry validation paths
- forwarding of response headers such as `Location` and `Set-Cookie`
- root free-form editor behavior at `/`
- posted instruction handling through `/`, including default denial of unsaved transient execution unless `DEVELOPER_DASHBOARD_ALLOW_TRANSIENT_URLS` is enabled
- saved bookmark browser edits through `/app/<id>/edit`, including named-route saves and non-transient play links when transient URL execution stays disabled
- smart-routed skill browser edits through `/app/<skill>/edit` and nested `/app/<skill>/<sub-skill>/edit`, including raw `/source` access and Play renders that stay on the smart alias instead of `/app/index`
- malformed bookmark icon bytes from older files are repaired into stable fallback glyphs on both `/app/<id>` and `/app/<id>/edit`, so browser verification should check for visible fallback icons instead of `�`
- nested saved bookmark ids such as `nav/foo.tt` through `/app/...`, `/app/.../edit`, and `/app/.../source`
- shared `nav/*.tt` bookmark rendering between top chrome and the main page body in sorted filename order
- raw `nav/*.tt` TT/HTML fragment rendering between top chrome and the main page body, plus direct `/app/nav/<name>.tt` and `/source` coverage for those raw fragment files
- Template Toolkit conditional rendering for shared nav fragments and saved pages using `env.current_page` and `env.runtime_context.current_page`
- `/apps -> /app/index` compatibility
- top chrome rendering on edit and saved render pages
- denial of browser `token=` and `atoken=` execution for transient page and action payloads, plus transient `/ajax?token=...`, when the transient URL opt-in env var is absent
- absence of accidental project-local `.developer-dashboard` creation when `dashboard restart` runs inside a git repo that has not opted into a local dashboard root
- saved bookmark `Ajax file => ...` handlers through `/ajax/<file>?type=...`, including `dashboards/ajax/...` storage, direct process-backed streamed ajax execution for both `stdout` and `stderr`, and blank-env verification under the default deny pol...
- file-backed saved Ajax Perl wrappers with autoflushed `STDOUT` and `STDERR`, including a timing check that long-running `print` plus `sleep` handlers emit early chunks instead of buffering until exit
- skill install progress rendering that keeps the epic checklist visible while
  streaming a rolling ten-line detail window under the active manifest step,
  with `CLI::Progress` coverage guarding both the rolling window and the
  collapse-on-success redraw

## Blank Environment Integration

Run the host-built tarball integration flow with:

```bash
integration/blank-env/run-host-integration.sh
```

This integration path builds the distribution tarball on the host with
`dzil build`, rebuilds `dd-int-test:latest` from the current
`integration/blank-env/Dockerfile`, runs that container with only the tarball
mounted into it, installs the tarball with `cpanm --notest`, and then
exercises the installed `dashboard` command inside the clean Perl container.
The blank-environment image must also carry the native CPAN build baseline
needed by packaged installs, including `libexpat1-dev`, `libssl-dev`,
`pkg-config`, and `zlib1g-dev`, so modules such as `XML::Parser` and
`Net::SSLeay` can build before the installed-runtime smoke reaches the staged
helper checks.
That image must also provide a real headless Chromium binary instead of an
Ubuntu snap launcher stub, so the browser smoke can execute inside the
container without requiring `snapd`.
The host-side launcher now also runs `prove -lv t/44-smart-router-two-stage.t`
immediately after `dzil build` and before the broader blank-environment
container flow. Treat that smart-router two-stage guard as a managed
post-build gate, not as an optional memory step.
That blank-container tarball install now assumes the normal `prove -lr t`
suite and explicit numeric `Devel::Cover` gate already passed in the source
tree. Its purpose is packaged dependency resolution and installed-runtime
verification, not rerunning the full tarball test suite a second time.
The release gather rules also exclude local `cover_db` output so a covered
host run does not contaminate the tarball under test.
The release gather rules must also exclude local scratch and dependency trees
such as `node_modules/` and `test_by_michael/`. Those paths are source-tree
implementation details, not distributable runtime assets, so release metadata
must fail before build or release if they are gathered into the tarball.

The shipped runtime-manager lifecycle checks now also fall back to `/proc`
socket ownership scans when that prebuilt image does not include `ss`, and
they re-probe the managed port for late listener pids before restart, so the
integration flow verifies the same stop/restart behavior that a minimal Linux
runtime will see in practice.
Those checks also cover the Starman master-worker split, where the recorded
managed pid can be the master while the bound listener pid is a separate
worker process on the same managed port.
RuntimeManager tests also lock shutdown signal portability by proving the
dashboard lifecycle maps named dashboard intents such as TERM and KILL to
numeric POSIX signals before calling Perl `kill`, matching Alpine/iSH Perl
builds that reject named signal strings.

The integration flow also:

- creates a fake project with its own `./.developer-dashboard` runtime tree
- creates that fake-project runtime tree only after `cpanm` completes, so the tarball's own test phase still runs against a clean runtime
- verifies installed CLI and saved bookmarks from that fake project's local runtime plus config collectors from that same runtime root
- verifies `dashboard version` reports the installed runtime version
- seeds a user-provided fake-project `./.developer-dashboard/cli/update` command plus `update.d` hooks inside the container and verifies `dashboard update` uses the same executable command-hook path as every other top-level subcommand, including late...
- verifies the installed web app denies `/?token=...` browser execution by default while saved bookmark routes still render
- uses headless Chromium to validate the editor, the saved fake-project bookmark page, and the helper login page
- verifies that an installed long-running saved `/ajax/...` route starts streaming visible output within the expected first seconds instead of buffering until process exit
- should be interpreted together with the tracked source-tree integration assets in `doc/integration-test-plan.md`, `doc/windows-testing.md`, and `integration/browser/run-bookmark-browser-smoke.pl`; source-tree tests now fail if those release/support...

## Windows Verification

For Windows-targeted changes, keep the verification layered:

- run the fast forced-Windows unit coverage in `t/`
- run the real Strawberry Perl smoke on a Windows host with `integration/windows/run-strawberry-smoke.ps1`
- when the checkout bootstrap changes, rerun that same smoke with `-UseInstallBootstrap` so the guest executes `install.ps1` through the streamed `Invoke-Expression` shape instead of only the file path
- run the full-system QEMU guest smoke with `integration/windows/run-host-windows-smoke.sh` before making a release-grade Windows compatibility claim

The Strawberry smoke verifies `dashboard shell ps`, `dashboard ps1`, one
PowerShell-backed collector command, one saved Ajax handler through
`Invoke-WebRequest`, and a browser DOM dump through Edge or Chrome when either
browser is present in the Windows environment.
In the Dockur-backed guest path, the launcher stages the Strawberry Perl MSI
from the Linux host and the Windows tarball install currently uses
`cpanm --notest` for third-party dependency setup before the real dashboard
runtime smoke runs. When `WINDOWS_USE_INSTALL_BOOTSTRAP=1` is set, the
in-guest smoke first runs the repo-root `install.ps1` through a streamed
`Invoke-Expression` wrapper with `DD_INSTALL_CPAN_TARGET` pointed at the
staged tarball, so the release gate matches the intended operator flow of
`irm .../install.ps1 | iex`. That bootstrap path must also prove that a
brand-new profile-loaded PowerShell session can resolve `dashboard`, print
`dashboard version`, run `dashboard logs` without a manual PATH edit, keep
`HOME` visible to later dashboard commands, and expose a working user-space
`make` shim for skill `Makefile` flows.
It must also keep test-only dependencies such as `Plack::Test` and
`Test::Pod` out of the packaged install prerequisite chain so blank Windows
guests do not fail while pulling `Test::SharedFork`.
The supported Windows runtime baseline is PowerShell plus Strawberry Perl.
Git Bash is optional. Scoop is optional. They remain setup helpers, not
runtime requirements for Developer Dashboard itself.



( run in 0.846 second using v1.01-cache-2.11-cpan-6aa56a78535 )