Skip to content

Analyzer follow-ups: cancel button lifetime, prompt byte-bound, empty-backlog sidecar wipe #46

Description

@vnovick

Follow-ups from the final review of the analyzer work in #43. That work is complete and green (make verify exit 0); these are the residuals it deliberately did not close, recorded so they do not evaporate.

Listed roughly by operator impact.


1. The Cancel button disappears on a page refresh while the job runs on

web/src/pages/Dashboard/components/DepsGraph.tsx gates the control on analyzeDeps.isPending && liveJob?.jobId !== undefined. useMutation has no mutationKey, so that state is local to the hook instance. A page reload, navigating off the Deps tab, or a transient poll failure all remove the Cancel control permanently — while the server-side job keeps running for up to its 10-minute deadline.

There is no server surface exposing the currently running job. depsanalysis.JobManager.Latest() exists but no route returns it, and the analyzer does not appear on the state snapshot.

Failure scenario: an operator starts an analysis, it looks stuck, they refresh the dashboard to investigate — and now there is no Cancel control and no indication a job is running at all.

Recovery technically exists but is undiscoverable: clicking "Analyze dependencies" re-attaches, because Enqueue returns the in-flight job's ID rather than starting a second pass. But the button says it starts a new analysis, so nobody would try it deliberately.

Fix: expose the running job (a GET /api/v1/deps/analyze/current, or put it on the snapshot) and derive the button from server state rather than mutation state. The 10-minute deadline is a real backstop, which is why this is not urgent — but it is the difference between "cancellable" and "cancellable if you don't blink".


2. Prompt size is bounded by issue count, not bytes

Chunking caps issues per turn at deps_analyzer_chunk_size (default 75), but AnalyzerIssue.Description (internal/depsanalysis/tracker_pass.go:17) is uncapped. 75 issues with long descriptions is still an unbounded prompt.

Acknowledged as an open question in the design rather than an oversight — the right cap wants a measurement rather than a guess. Recording it so the "prompt is bounded" claim is not read as stronger than it is.


3. An empty backlog silently overwrites a good sidecar

If FetchIssues transiently returns nothing — states misconfigured after a settings change, an empty tracker page — both analyzer paths write an empty sidecar and log success. A previously good inferred-edge set is wiped, and the dashboard shows a complete-looking result over nothing examined.

Both paths do this consistently (cmd/itervox/init_deps_analysis.go says so explicitly: "Still write a sidecar so DepsLastAnalyzedAt populates correctly"), so it is a deliberate existing choice rather than a new bug. But it is the degenerate case of exactly what the chunk-failure atomicity rule protects against elsewhere.


4. The analyzer log-dir wiring has no test coverage

cmd/itervox/deps_analyzer_service.go computes logDir = filepath.Join(s.agentLogDir, "deps-analyzer"), but the test constructor passes agentLogDir: "", so that branch never executes in any test. wireDepsAnalyzerService and the agentSessionsDir threading in main.go have no test caller either.

Deleting the branch, or joining the wrong path, leaves the suite green — so the "analyzer output is now written to disk" deliverable is verifiable only by inspection.


5. Coverage numbers do not cover the new UI

web/src/pages/Dashboard/** is excluded from the coverage report by web/vitest.config.ts:84 (pre-existing, repo-wide). So the aggregate figure says nothing about DepsGraph.tsx.

The component does have its own tests and they pass; the point is only that the gate number should not be read as evidence about it. Reversing a repo-wide exclusion would drag every other Dashboard page's uncovered lines into the gate at once, so this is a note rather than a proposed change.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions