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.
Follow-ups from the final review of the analyzer work in #43. That work is complete and green (
make verifyexit 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.tsxgates the control onanalyzeDeps.isPending && liveJob?.jobId !== undefined.useMutationhas nomutationKey, 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
Enqueuereturns 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), butAnalyzerIssue.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
FetchIssuestransiently 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.gosays 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.gocomputeslogDir = filepath.Join(s.agentLogDir, "deps-analyzer"), but the test constructor passesagentLogDir: "", so that branch never executes in any test.wireDepsAnalyzerServiceand theagentSessionsDirthreading inmain.gohave 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 byweb/vitest.config.ts:84(pre-existing, repo-wide). So the aggregate figure says nothing aboutDepsGraph.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.