# Dashboard Improvement Plan

Repository: `/home/mv/projects/developer-dashboard`
Focus: startup performance of the public `dashboard` command

## Executive Summary

The public `dashboard` command is thin by design, but it still matters because it is the most frequently used entrypoint. Profiling shows that the switchboard itself is not the majority of end-to-end time for a normal built-in command. The bigger cost is split between Perl module loading and repeated current-directory resolution, with additional command-specific cost inside the helper process after handoff.

Probe | Observed Time | Meaning
--- | --- | ---
`dashboard version` | about 110 ms average | Special-case fast path. Useful baseline, but not representative of normal helper dispatch.
`dashboard paths` | about 493 ms average | Representative built-in command path through the switchboard and staged helper.
Direct helper: `.../cli/dd/paths` | about 362 ms average | Shows most of the cost is after the public switchboard hands off.
`perl -Ilib -c bin/dashboard` | about 120 ms | Compile/load overhead is a major part of the switchboard cost.
Switchboard module load | about 77 ms | Static startup cost from Perl module loading before command work begins.
Isolated env/path/helper setup | about 23 ms total | `PathRegistry`, `EnvLoader`, and `ensure_helpers` are not the main cost in isolation.

## Problem

The command is thin in architecture, but the real startup path still pays a measurable tax before useful work happens. This matters because users hit `dashboard` for nearly everything, so even modest startup delays become visible and repetitive friction.

### Primary Problem

Normal built-in command dispatch is slower than expected because the runtime repeatedly resolves the current working directory and project context, and that cost compounds across both the public switchboard and the helper process.

### Secondary Problem

The command-specific helper runtime is doing enough repeated path recomputation that even a lightweight command such as `dashboard paths` still feels expensive.

## Evidence

1. A strace of `dashboard paths` showed `/bin/pwd` executing 72 times during one command invocation.
2. Split of those `pwd` executions:
   - 6 before helper handoff
   - 66 after helper handoff
3. A direct probe confirmed that `Cwd::cwd()` on this machine forks `/bin/pwd`.
4. A direct microbenchmark showed `cwd()` averaging about 2.0 ms per call on this machine.
5. The `paths` helper alone took about 212 ms when invoked directly through its Perl module path.

## Root Cause

### 1. Repeated `cwd()` calls

The largest concrete hotspot is repeated `Cwd::cwd()` use. On this environment, that is not a cheap in-process call. It forks `/bin/pwd`. That makes otherwise innocent path lookups surprisingly expensive.

### 2. Repeated project/runtime path derivation

`PathRegistry` methods derive current project roots, runtime layers, state roots, and related values repeatedly. Commands such as `dashboard paths` ask for many of these values in one call, and that compounds the `cwd()` overhead.

### 3. Startup compile/load cost

The switchboard still pays a meaningful Perl compile/load cost before it can decide where to hand off. That cost is not the majority of total runtime for a normal helper path, but it is a large chunk of the public entrypoint overhead.

### 4. Missed caching opportunity

`Developer::Dashboard::CLI::Paths::_build_paths()` passes `cwd => cwd()` into `PathRegistry->new()`, but `PathRegistry` does not appear to store or reuse that value. So the helper pays for `cwd()` but does not gain any caching from it.

## What Is Not The Main Problem

- `EnvLoader` by itself is not the primary cost center.
- `InternalCLI::ensure_helpers()` is not the primary cost center in isolation.
- The thin switchboard architecture itself is not the problem. The issue is the repeated runtime work done around path discovery and module startup.

## Recommendation

Keep the public `dashboard` command thin. Do not replace the switchboard design. Instead, reduce repeated startup work, especially current-directory and project-root recomputation.

### Recommended Priority Order

1. Eliminate repeated `cwd()` calls from the startup and helper path.
2. Add request-local caching inside `PathRegistry` for cwd, project root, runtime layers, and derived roots.
3. Audit helper commands like `paths` for repeated calls into path-derivation methods that can be computed once.
4. Only after that, consider deeper module-load reduction in `bin/dashboard`.

## Solution Plan

### Phase 1: Remove High-Volume `cwd()` Cost

- Store a single cwd value per invocation and reuse it throughout the command lifetime.
- Teach `PathRegistry` to accept and actually use a precomputed cwd.
- Replace repeated internal `cwd()` lookups with the cached value wherever the command is operating in one fixed current directory.

### Phase 2: Add Request-Local PathRegistry Memoization

- Memoize:
  - current project root
  - runtime layers
  - runtime root
  - state root and derived roots
  - CLI root and similar derived directories
- Keep memoization strictly per-process and per-invocation so it does not create stale cross-command state.

### Phase 3: Reduce Repeated Helper-Side Computation

- For helper commands like `paths`, compute the path inventory once and reuse that data structure rather than calling multiple lazy getters that each recompute upstream state.
- Review `all_paths()` and related accessors for repeated tree walks and repeated state-root creation.

### Phase 4: Module Load Cleanup

- Move non-essential `use` statements behind `require` when the command path does not always need them.
- Keep the switchboard’s eager module set as small as possible.
- Preserve readability and safety; do not trade away clear error behavior for micro-optimizations.

## Expected Outcome

The biggest likely gain is from removing repeated `/bin/pwd` subprocess calls. Since the trace showed 72 `pwd` executions for one `dashboard paths` command, reducing that to one or a small handful should materially reduce wall time. After that, request-local caching should cut the remaining repeated path derivation overhead.

## Implementation Notes

- Any optimization work should preserve the DD-OOP-LAYERS behavior and lookup order.
- Do not hide errors or weaken explicit runtime checks.
- Re-profile after each phase instead of batching all changes blindly.

## Suggested Verification After Changes

1. Re-run timing probes for `dashboard version` and `dashboard paths`.
2. Re-run syscall trace and count `/bin/pwd` executions.
3. Verify helper behavior and path outputs remain identical.
4. Run the relevant CLI and path regression tests.

## Conclusion

The public `dashboard` command is not slow because the switchboard pattern is wrong. It is slow because repeated path discovery work, especially `cwd()` on this environment, is expensive and occurs many times per invocation. The strongest plan is to keep the architecture, remove repeated cwd/process calls, add request-local path caching, and then trim module startup cost where justified.