Skip to content

pa-types/pa-cli/pa-tui: daemon incident forensics — prime-agent incident CLI + agents-view incident notices (TS #2406 port) - #2866

Merged
kevinjosethomas merged 17 commits into
rustfrom
lane/incident-cli-port
Sep 26, 2026
Merged

kevinjosethomas merged 17 commits into
rustfrom
lane/incident-cli-port

Conversation

@kevinjosethomas

@kevinjosethomas kevinjosethomas commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Summary

Rust port of TS PR #2406 — daemon incident forensics: the prime-agent incident timeline CLI and the agents-view incident notices, built on one shared classifier.

prime-agent incident [--since <time>] [--until <time>] [--session <id>] reconstructs daemon-log forensics for a time window into an operator timeline — supervisor events, session anomalies, and recovery — every line timestamped and severity-tagged, repeated identical events aggregated (x4, until ...), no raw-log spam. The agents view reuses the same classifier to poll agent.jsonl every 30 seconds (rotation-safe, bounded, incremental) and shows one collapsed, dismissible warning line in the header when the recent log shows a worker crash, a command-timeout burst, or an update restart, pointing at the CLI for the full timeline. Esc dismisses the notice when the editor is idle and never steals priority from an armed delete confirmation.

TS → Rust mapping

TS (PR #2406) Rust
src/cli/incident.ts (1279L) — shared core crates/pa-types/src/incident/{mod,parse,patterns,classify,classify_tests,anomaly}.rs
src/cli/incident.ts — CLI half (time bounds, report, log discovery, run) crates/pa-cli/src/incident/{mod,time,report,report_tests,source_tests}.rs
src/cli/command-registry.ts (+13) crates/pa-cli/src/command_registry.rs (the incident spec, verbatim TS help strings)
src/cli/public-command.ts (+24) crates/pa-cli/src/public_command.rs (routing, one-shot window resolution, dispatch tests)
src/modes/agents-view/incident-notices.ts (+466) crates/pa-tui/src/incident_notices.rs (+ incident_notice_tests.rs)
src/modes/agents-view/agents-view-mode.ts (+84/-2) crates/pa-tui/src/agents_view.rs + crates/pa-cli/src/interactive_mode.rs (state carried across the view/session loop = TS persistentState.incidentNoticeState)
test/incident.test.ts (+735) split by crate ownership: pa-types incident unit tests + pa-cli report/window/session-filter/source/dispatch tests
test/agents-view-incident-notice.test.ts (+297) crates/pa-tui/src/incident_notice_tests.rs + new crates/pa-tui/tests/agents_view_incident_notice_e2e.rs (mock supervisor, headless frames)
test/public-command.test.ts (+70) crates/pa-cli/src/public_command.rs incident_dispatch_tests
.changes/*.md (+2), test/agents-view-mode.test.ts (+1 mock line) no Rust counterpart (the Rust repo has no .changes dir; the mock change is TS-harness-only)

Crate placement: the classifier lives in pa-types::incident because pa-tui depends on pa-types alone (AGENTS.md: pa-types is the only shared crate) — the TS product keeps the same single classifier in cli/incident.ts with both halves importing it, and the port preserves that: the agents-view notice reuses the classifier, never re-implementing it (TS incident-notices.ts imports from ../../cli/incident.js). The CLI's log-file discovery, --since/--until window parsing, and report rendering belong to pa-cli; the notice polling, rotation-safe incremental reads, and dismissal horizons belong to pa-tui. README scope entries updated (pa-types + pa-cli).

Semantic parity carried from the TS PR

  • Sources: primary ~/.prime/agent/logs/agent.jsonl including its .old rotation; fallback to the newest per-daemon log (<socket>.<hash>.log, hash-suffix match so .sock-less basenames and Windows named-pipe sockets match), with the is_file guard so a directory matching the pattern cannot hide valid logs. Window filtering happens later; the fallback gates on parse yield only.
  • Classification: the TS regexes kept 1:1 via the regex crate — supervisor start/failed-spawn (lock-held arm), supervisor/daemon command failures (timeout/auth/starting/recovering/update-prep/unknown-session/quoted names), stderr forward lifecycle (start/crash/shutdown exit/signal/stop-requested/passivation with the empty-name token guard), catch-up and heartbeat-list timeouts, recovery replay (the ranked, capped operation breakdown), adopt/recover failures, unresponsive park, stale-registration reclaim, job migration, supervisor replacement, scheduled wake, evictions, worker-own lifecycle lines, unknown diagnostics.
  • pid→worker attribution keyed on sighting time (a pid reused by a replacement worker never back-attributes), including Windows named-pipe socket paths.
  • Anomalies: command-timeout stalls (the densest 30-minute run; anchored at the cluster's first timeout, "N command timeouts over X"), error bursts (3+ within 10 minutes, "N warnings/errors over X"), and 10-minute session event gaps. Per-subject keying keeps daemons sharing one agent.jsonl separate.
  • Lifecycle dedupe: the daemon writes the same lifecycle event to the structured log and the stderr forward — the second copy drops within 2s across components, while a same-component restart within 2s is a real event.
  • Window/--session: UTC time-bound parsing (ISO date-time, date, bare HH:MM today, fractional seconds, Z/±HH[:MM] offsets with RFC-3339 minute validation and roundtrip date validation); session filters prefix-match in both directions over session ids, worker ids, and quoted session names; the empty-token guard keeps name="" from matching every filter.
  • The report: Prime Agent incident timeline header, Window: MM-DD HH:MM:SS → MM-DD HH:MM:SS UTC (30m), the Source: line with scanned/in-window/skipped counts, the three sections with (none) placeholders, (xN, until ...) aggregation, and chalk-honoring severity colors (red 31 / yellow 33 / dim 2, off-TTY and under NO_COLOR — the daemon_discovery format.rs rule).
  • Notices: 24h window, 512KB bounded tail, 30s poll, 20k-entry retention cap, one collapsed line (severity rank, then recency), dismissal horizons keyed kind|subject (a timeout-burst notice anchors at its stall cluster's latest timeout so a later timeout extending the burst re-surfaces it, while an isolated stray timeout never moves the anchor), the first-read .old bridge with the same-generation guard (a rename rotation between the live read and the bridge read must not double supervisor starts into a phantom update restart), the mid-rotation offset continuation (the un-consumed tail of a generation that rotates out between polls), a missing/unreadable log keeping the consumed offset (never fabricating a restart) while the notice ages out with its window.
  • Esc (tui.select.cancel) dismisses only with an empty search prompt and no armed delete confirmation; the confirm wins (as in TS); the status line reads Incident notice dismissed. The state survives view re-entry through the flow (TS persistentState.incidentNoticeState).

Tests

  • pa-types: parsing (ISO shapes, malformed-line tolerance, impossible dates), pid attribution (ownership at event time, reuse, named-pipe paths), classification arms (command failures, lifecycle lines, stack-frame continuation, the 533-operation recovery breakdown with the 4-kind cap), anomalies (stall/burst/gap, per-socket separation, isolated-failures-never-merge), latest-stall anchoring.
  • pa-cli: report suites (the TS green-run fixture, the full 2026-09-10 crash+recovery fixture — x4 attach aggregation, the 2339fb7da605 catch-up stall, the one-time critical EPIPE crash, the provider-failure attribution counts, the 533-op breakdown, the adopt failure), window/session filtering (prefix match, quoted names, empty-name passivation, worker command-failure tokens, the clear no-reference message), log-source discovery over fixture agent dirs (skipped counts, the newest-per-daemon fallback incl. a future-dated decoy directory filtered by is_file via File::set_times, .sock-less socket basenames, empty/unreadable agent.jsonl, no-fallback-when-it-parses, missing logs), options/window parsing (the TS parseIncidentTimeBound suite incl. offset-minute rejection), and the public-command dispatch (routing + exit-1 usage arms + the help listing).
  • pa-tui: notice derivation (multi-socket stall anchoring, update-restart only from repeated successful starts), dismissal horizon semantics (extending burst re-surfaces, isolated timeout stays hidden), rotation reads (.old tail without a trailing newline, the hard-link same-generation bridge skip, the mid-rotation stranded tail), the missing-log offset preservation, the tail-bound cut; plus a headless e2e over a mock supervisor: the rendered collapsed line with the pointer, Esc dismissal with the status line, dismissal persistence across a second view run with the carried state, and the armed-delete-confirm case keeping the notice.
  • TS test fixtures ride the real clock where the TS tests use Date.now(); the rest are pinned to the TS fixtures' 2026-09-10/16 instants.

Ownership compliance

  • pa-types: new incident module — the shared classifier; README scope entry added; the only crate pa-tui can share with pa-cli. Type: new pub surface (the classifier API: parse/classify/collect/anomaly entry points + types).
  • pa-cli: incident CLI module (crate-private), the public-command route, the registry spec; README scope entry added.
  • pa-tui: incident_notices module (pub — the agents view and the flow carry IncidentNoticeState), the agents-view wiring.
  • Dependency change (architectural, per AGENTS.md): regex = "1" added to pa-types. Rationale: the TS classifier matches ~30 log-message regexes; the regex crate keeps their semantics 1:1 where hand-rolled parsers would risk behavioral drift. regex 1.13.1 is already in the tree (pa-tui), so no new crates enter the build — the lock gains exactly the pa-types → regex edge. make deny should stay clean (MIT/Apache-2.0, no advisories).
  • Cargo.lock updated in the same change with exactly the edge cargo would write.

Deliberate deviations from the TS PR

  1. formatIncidentNoticeTime renders UTC, not the local wall clock. The Rust tree is UTC end-to-end (the daemon's now_iso() logs UTC; no timezone layer exists anywhere in the crate graph), so the notice's crashed at HH:MM reads UTC and the day/year prefixes follow the UTC calendar. The TS shape (same-day bare time, M/D HH:MM, YY/M/D HH:MM) is preserved. Documented at the function.
  2. The TS .changes/*.md changelog entries have no Rust counterpart (the Rust repo has no .changes directory; release notes come from the PR/commit stream).
  3. The setInterval poll is a tokio::select! wake-up plus a deadline drain in the view loop (a deadline-driven interval, so a busy input stream cannot starve the 30s cadence — the TS interval fires between event-loop turns regardless).

Validation

  • cargo fmt --all --check clean on the folded head (182d1532c on rust tip 1ce0d94b1).
  • clippy/test gates: not run locally (host cargo is prohibited on this lane); CI at the exact head is the gate.
  • Parity-diff evidence: this port is a source-level faithful port of TS PR [RSI, feature] Daemon incident forensics: prime-agent incident CLI and agents-view notices #2406 (the TS binary's incident command output was the fixture basis for the exact-layout report test); the daemon-log fallback reads the same per-daemon log format the Rust supervisor writes (paths.rs daemon_log_path + RotatingLog, [<ISO>] msg lines — the TS supervisor: prefix arm is kept for the TS-emitted logs the fallback may encounter).

Note

Medium Risk
Large new surface area (log parsing, rotation, and classification) with many edge cases, but read-only forensics and local log reads—no auth or remote data changes.

Overview
Ports TS daemon incident forensics to Rust: prime-agent incident rebuilds an operator timeline from daemon logs, and the agents view surfaces a dismissible header warning when recent logs show crashes, timeout bursts, or update restarts.

pa-types::incident adds the shared classifier (JSONL and per-daemon log parsing, event classification, pid attribution, stall/burst/gap anomalies) with regex for TS-parity message patterns. pa-cli implements log discovery (agent.jsonl + .old, fallback to newest *.hash.log), UTC --since/--until/--session handling, and the colored, aggregated report. pa-tui::incident_notices polls agent.jsonl on a 30s cadence with rotation-safe incremental reads and dismissal horizons; agents_view renders the notice, Esc dismisses when safe, and interactive_mode carries incident_notice_state across view re-entries.

Help/registry wiring, README scope updates, and broad unit/e2e tests mirror the TS fixtures and dispatch behavior.

Reviewed by Cursor Bugbot for commit 068259a. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add incident forensics CLI and agents-view incident notices (TS #2406 port)

Ports the TypeScript daemon incident forensics feature to Rust.

  • Adds prime-agent incident with --since, --until, and --session. Defaults to the last 24 hours and rejects windows where --until is at or before --since (public_command.rs).
  • Adds the shared incident module to pa-types: log parsing (JSONL and plain-text daemon lines), log-message classifiers, worker-pid attribution, and anomaly computation for timeout stalls, error bursts, and session gaps (parse.rs, classify.rs, anomaly.rs).
  • The report renders a UTC timeline with severity colors (disabled under NO_COLOR or non-terminal stdout), aggregated repeats, and source line counts (report.rs).
  • Agents view now shows an active incident notice in the header, refreshes it every 30 seconds from the agent log, and lets Esc dismiss it when no query or delete confirmation is active. Dismissal and read offset persist across view/session transitions (agents_view.rs, incident_notices.rs).
  • Behavioral Change: AgentsViewOptions gains a new incident-notice state field; all existing test fixtures updated. Esc handling gains an early dismissal path before delete handling. ANSI styling and a new 30-second poll are added to the agents view.

Macroscope summarized 068259a.

…ncident + agents-view incident notices (TS #2406 port)

Port TS PR #2406 ([RSI, feature] Daemon incident forensics) to Rust:

- pa-types::incident (new): the shared classifier of TS src/cli/incident.ts
  — agent.jsonl + per-daemon log-line parsing, worker pid attribution, event
  classification (the TS regexes kept 1:1 via the regex crate), the
  stall/burst/gap anomaly computation, and the severity/category vocabulary.
  Shared by the CLI and the agents-view notice, like the TS module (pa-tui
  depends on pa-types alone).
- pa-cli::incident (new): the CLI half — --since/--until time-bound parsing
  (UTC, per the daemon log), the timeline report (aggregation, sections,
  chalk-honoring severity colors), log-source discovery (agent.jsonl plus its
  .old rotation, falling back to the newest per-daemon log), the
  options/window parsing, and the run entry. Routed as the "incident" command
  in public-command with a one-shot window resolution; the command spec
  joins the registry.
- pa-tui::incident_notices (new): port of
  src/modes/agents-view/incident-notices.ts — reuse of the classifier (never
  re-implemented), the 30s poll with rotation-safe bounded incremental reads
  (the .old bridge, the same-generation guard, the mid-rotation offset
  continuation), the collapse to one dismissible warning line, and the
  dismissal horizons.
- pa-tui agents_view: the notice renders under the splash (wrap, one-column
  gutter, warning color, pointer to the CLI); Esc (tui.select.cancel) dismisses
  it when the search is empty and no delete confirm is armed (the confirm wins,
  as in TS); the 30s poll re-derives and renders on change; the state rides the
  options/outcome so the pa-cli agents-view flow carries it across re-entry
  (TS persistentState.incidentNoticeState).
- tests: the TS incident.test.ts suites split by crate ownership (parse/
  classify/anomaly units in pa-types; report/window/session-filter, the
  crash+recovery fixture, log-source discovery, and dispatch in pa-cli), the
  agents-view-incident-notice.test.ts derivation/rotation units in pa-tui, and
  a headless e2e over a mock supervisor covering render, Esc dismissal,
  dismissal persistence across re-entry, and the armed-delete-confirm case.
- pa-types gains the regex dependency (already in the tree via pa-tui) for
  the TS patterns; pa-types/pa-cli README scope entries updated.

Rust-only: no TS edits. No host cargo (fmt --check only).
@macroscopeapp

macroscopeapp Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Macroscope skipped reviewing this pull request. Per-review cost limit exceeded (workspace setting).

This review would cost an estimated $13.75, which exceeds your per-review limit of $10.00.

The top 3 files driving up this estimate:

File Diff Size Estimate
crates/pa-types/src/incident/classify.rs 29.96KB $1.80
crates/pa-tui/src/incident_notices.rs 28.56KB $1.71
crates/pa-cli/src/incident/report_tests.rs 17.03KB $1.02

Tip

To get this pull request reviewed, you can:

  1. Comment @macroscope-app on this PR to request a manual review (monthly spend limits still apply).
  2. Exclude the file(s) above from review by adding a pattern to your .macroscope/ignore.md — note that creating this file replaces Macroscope's built-in default ignores rather than extending them.
  3. Raise your cost limit in your workspace billing settings.

Turn off this reminder going forward

@github-actions

github-actions Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Prime Agent performance — partial

PR 068259ac compared with main cd1f215c.

Benchmark execution did not complete successfully. Missing measurements are not performance wins.

Failure diagnostics:

  • pr setup: RuntimeError: pr prepare 0 failed with exit code 1
  • PR: CalledProcessError: Command '['/usr/sbin/runuser', '-u', 'builder', '--', 'npm', 'ci', '--no-audit', '--no-fund']' returned non-zero exit status 1.

See the saved per-trial logs and terminal transcripts for details.

Overall: 0 regressed · 0 improved · 0 no clear change · 42 unavailable.

Metric Main This PR Change
Cold startup 658.2 ms — —
Warm startup 505.9 ms — —
Installation 5.54 s — —
Compressed release artifacts 73.12 MB — —
Installed footprint 595.66 MB — —
Idle memory, summed RSS 653.11 MB — —

Python runtime

Metric Main This PR Change
Python kernel startup 30.6 ms — —
Python cell round trip 0.070 ms — —
Empty bash command 1.8 ms — —
Bash git status 2.4 ms — —
Bash 32 KiB output 1.8 ms — —
35 cells / 9 shell calls 23.0 ms — —
Python interrupt to done 0.479 ms — —
Python state snapshot 9.2 ms — —
Python state restore 117.2 ms — —
Python idle RSS 21.30 MB — —
Python RSS after pandas workload 75.59 MB — —

Session transport

Metric Main This PR Change
Full-history transfers per warm session switch 1.00 transfers — —
Private frame decode, 32 MiB in 8 KiB chunks 15.7 ms — —

UI interactions

Metric Main This PR Change
Resume large session (cold) 1,668.0 ms — —
CPU, resume large session 2,040.0 ms — —
Switch into large session 1,367.0 ms — —
CPU, switch into large session 1,500.0 ms — —
Open agents view from a session 131.6 ms — —
CPU, open agents view 50.0 ms — —
Full agents roster, many sessions 4.03 s — —
CPU, full agents roster 0.97 s — —
Open another session from agents view 1,823.3 ms — —
CPU, open from agents view 1,020.0 ms — —
Reopen resident large session 193.5 ms — —
CPU, reopen resident session 210.0 ms — —
Open subagent session at depth 6 17,427.7 ms — —
CPU, open subagent at depth 6 4,580.0 ms — —
Open chain parent from agents view 2,973.7 ms — —
CPU, open chain parent 1,400.0 ms — —
Scheduled catalog, first request 411.8 ms — —
CPU, scheduled catalog 800.0 ms — —
Scheduled catalog, repeated request 0.5 ms — —
CPU, repeated catalog 0.0 ms — —
Cold worker with three catalog scans 426.7 ms — —
CPU, cold worker and scans 460.0 ms — —
UI memory after interactions 1,713.04 MB — —

Sandbox cost: ~$0.0498 — no inference calls.
Run, logs, and downloadable raw results

Methodology and samples

Main resolved at 2026-09-26T19:31:20.310727+00:00. Harness cd1f215c.
Linux x64, 4 vCPU, 8 GB RAM, 20 GB disk; region us.
Image: node:24-bookworm@sha256:be23f54a88d34e8824c741b19b91064094f92c1c97b194144bfc8b50d67258e2.
Stock tools, skills, daemon, and Python bootstrap enabled; fresh homes and a fixed Git fixture.
Onboarding is dismissed; the editor starts without a selected model or submitted prompt.
Medians shown. Arrows require a 20% timing/memory change plus absolute floors and IQR.
These practical noise floors are not a statistical significance test.
Cold means stopped Prime processes; OS filesystem caches are not flushed.
No model requests or credentials. Installation excludes build/setup time.
Installer tarballs use loopback; npm/Python downloads use the network with fresh caches.
Artifact size counts release tarballs; footprint after first use includes registry packages.
MB is decimal. Summed RSS can double-count shared pages; PSS is recorded when available.
Provisioning, setup, and build durations are recorded separately in the raw results.
Kernel probes use the installed JSONL runtime, outside the TUI/TypeScript host.
Per trial: 50 Python cells, 5 calls per shell case, and one 35-cell mix (9 git status calls).
Cell/shell values are batch means; other runtime timings are single operations.
State fixture: a 10,000-row × 8-column integer DataFrame and a 10,000-integer list.
Restore runs in a fresh kernel, including pandas imports; kernel startup is excluded.
Kernel RSS covers the isolated Python process; loaded RSS follows the pandas workload.
Transport benches run node against the prepared source build, outside the installed home.
The switch benchmark drives one warm switch into a 48k-entry session through a real
daemon and counts full-history crossings: streamed replacement snapshots, inline
replacements, and full-history refetch responses.
Frame decode times one 32 MiB private frame, snapshot-chunk header, pushed in
8 KiB chunks; the wire shape of multi-MB frames on the daemon-worker channels.
UI trials use a fresh fixture set: 194 top-level sessions including one ~40 MB transcript,
40 ledger fan-out children, and a 6-deep subagent chain (~46 spawn edges).
Large fixtures hold 1,999 complete triples (~5 MB JSONL); medium 119; subagents 399 each.
Interactions: cold --resume of a large session, warm /resume switch, left-arrow to agents view,
roster settle with many saved sessions, search-and-open of another large session,
reattaching to that resident session, opening the chain parent, and drilling to depth 6.
Readiness is the rendered transcript tail plus a confirmed editor echo.
CPU metrics sum utime+stime across the whole benchmark-user process tree per interaction.
UI memory sums RSS after the interactions; PTY byte counts are in the raw results.
A separate catalog fixture has 2,300 sessions, 2,298 edges, and 13 paused scheduled-job owners.
Catalog timings cover first/repeated reads and cold worker creation under three pending scans.
All expected jobs and owner metadata are checked; worker readiness excludes TUI rendering.
Costs estimate full sandbox lifetimes at configured rates, including setup and build.
Budget target: $1; not a billing cap. Performance changes are informational.
Failed or incomplete execution fails the workflow; saved artifacts remain available.
Each side stops a phase after 2 identical consecutive failures.
Skipped trials are not attempted samples. Warm startup requires a successful cold launch.

Metric Main successful/attempted PR successful/attempted Main spread PR spread
Cold startup 10/10 0/0 IQR 22.1 ms —
Warm startup 10/10 0/0 IQR 31.4 ms —
Installation 3/3 0/0 range 0.90 s —
Compressed release artifacts 1/1 0/0 — —
Installed footprint 1/1 0/0 — —
Idle memory, summed RSS 10/10 0/0 IQR 7.24 MB —
Python kernel startup 10/10 0/0 IQR 1.2 ms —
Python cell round trip 10/10 0/0 IQR 0.022 ms —
Empty bash command 10/10 0/0 IQR 0.2 ms —
Bash git status 10/10 0/0 IQR 0.2 ms —
Bash 32 KiB output 10/10 0/0 IQR 0.1 ms —
35 cells / 9 shell calls 10/10 0/0 IQR 2.4 ms —
Python interrupt to done 10/10 0/0 IQR 0.061 ms —
Python state snapshot 10/10 0/0 IQR 0.4 ms —
Python state restore 10/10 0/0 IQR 11.2 ms —
Python idle RSS 10/10 0/0 IQR 0.12 MB —
Python RSS after pandas workload 10/10 0/0 IQR 0.13 MB —
Full-history transfers per warm session switch 10/10 0/0 IQR 0.00 transfers —
Private frame decode, 32 MiB in 8 KiB chunks 10/10 0/0 IQR 2.3 ms —
Resume large session (cold) 3/3 0/0 range 341.5 ms —
CPU, resume large session 3/3 0/0 range 110.0 ms —
Switch into large session 3/3 0/0 range 64.4 ms —
CPU, switch into large session 3/3 0/0 range 190.0 ms —
Open agents view from a session 3/3 0/0 range 7.2 ms —
CPU, open agents view 3/3 0/0 range 50.0 ms —
Full agents roster, many sessions 3/3 0/0 range 0.41 s —
CPU, full agents roster 3/3 0/0 range 0.09 s —
Open another session from agents view 3/3 0/0 range 55.6 ms —
CPU, open from agents view 3/3 0/0 range 30.0 ms —
Reopen resident large session 3/3 0/0 range 36.3 ms —
CPU, reopen resident session 3/3 0/0 range 20.0 ms —
Open subagent session at depth 6 3/3 0/0 range 293.1 ms —
CPU, open subagent at depth 6 3/3 0/0 range 210.0 ms —
Open chain parent from agents view 3/3 0/0 range 143.5 ms —
CPU, open chain parent 3/3 0/0 range 90.0 ms —
Scheduled catalog, first request 3/3 0/0 range 38.1 ms —
CPU, scheduled catalog 3/3 0/0 range 50.0 ms —
Scheduled catalog, repeated request 3/3 0/0 range 0.1 ms —
CPU, repeated catalog 3/3 0/0 range 0.0 ms —
Cold worker with three catalog scans 3/3 0/0 range 21.6 ms —
CPU, cold worker and scans 3/3 0/0 range 10.0 ms —
UI memory after interactions 3/3 0/0 range 12.20 MB —

Failures:

  • pr setup: RuntimeError: pr prepare 0 failed with exit code 1
  • PR: CalledProcessError: Command '['/usr/sbin/runuser', '-u', 'builder', '--', 'npm', 'ci', '--no-audit', '--no-fund']' returned non-zero exit status 1.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/pa-types/src/incident/classify.rs Outdated
Comment thread crates/pa-types/src/incident/parse.rs Outdated
Comment thread crates/pa-types/src/incident/classify.rs
Comment thread crates/pa-tui/src/incident_notices.rs
lane-agent and others added 9 commits September 26, 2026 04:27
The first CI pass (no host cargo on this lane — the compiler is CI) found
four pa-types errors and the audit behind them found more downstream:

- pa-types parse: the take_digits `?` yields u32 into an i64 annotation.
- pa-types anomaly tests: contains() takes a reference to the element,
  not the element's reference.
- pa-types classify tests: no moving a tuple out of a shared reference
  (build the fields Vec instead).
- pa-cli time: the same u32/i64 year typing on the time-bound parser
  (found in the audit, ahead of the second CI pass).
- pa-cli source tests: an array mixing &str literals with a &String
  from a helper call; the future-mtime decoy-dir test is unix-gated
  (Windows cannot open a directory with File::open) per the #2833
  pattern.
- The epoch fixtures were miscalculated by 5h20m (2026-09-10T20:00Z is
  1_789_070_400_000, 2026-09-16T22:30Z is 1_789_597_800_000): every
  constant now verified against an authoritative datetime; the
  is_daemon_log_file_name expectation that contradicted the TS
  hash-suffix pattern is corrected.
- report_for rode Default::default() (an empty window) instead of the
  default 20:00-20:30 window.
- clippy (-D warnings) hygiene: map().unwrap_or*/map().unwrap_or_else
  chains -> map_or/map_or_else (map_unwrap_or), the two-arm match with a
  wildcard in the rotation read -> a let-else filter
  (single_match_else), a mid-function use item -> std::fs::write
  (items_after_statements), and an unused Arc import in the e2e.
- The e2e mock serves one connection per view run (the dismissal test
  runs the view twice; a selection-less exit closes the link).
pa-types and pa-cli compile clean at the round-2 head; pa-tui had two
root errors behind twelve diagnostics:

- format_incident_notice_time addressed the date parts as named fields
  on the notice_parts tuple — destructure them instead.
- AgentsViewMode::new takes the state out of options (the TS ??= lazy
  init) — the parameter is mut now.
The workspace compiles at the round-3 head; the remaining failures
were seven test bugs and one runtime root cause:

- IncidentSeverity's Display impl used write_str, which ignores the
  report's {:8} severity padding — f.pad honors it (the layout test's
  missing four spaces).
- The startup-blocked lock check is case-insensitive in TS
  (/lock file is already being held/i): real log lines carry
  'Lock file is already being held', so the lowercase compare restores
  the arm (two source-test failures).
- The date-only timestamp fixture is UTC midnight, not 20:00.
- The named-pipe crash fixture rode the supervisor component without a
  socket path; the TS test uses the daemon component (daemonLine).
- TS's duration formatter keeps only hours+minutes once hours overflow
  (1h1m, never 1h1m40s).
- A notice expiring out of its window IS a changed line (Some -> None),
  so the poll reports it; the test asserted the opposite.
- The newest-per-daemon fallback pick now pins the fixture's mtimes
  (the write-order timing raced the runner's filesystem granularity).
- The e2e respond_failure call carries its 4th argument; the unused
  crash-line helper in the notice tests is gone (dead-code warning).
- The fractional-seconds scaling in timestamp_to_ms scaled the wrong
  way (.7 parsed as 7ms, not 700ms): keep the first three digits and
  pad to the right, like Date.parse and the TS padEnd(3, "0").
- needless_borrow: error_message(first_line(err)) is already a &str.
- manual_repeat_n: repeat().take() is repeat_n in the recovery-breakdown
  fixtures (pa-types + pa-cli).
unwrap_or_else(IncidentNoticeState::new) is unwrap_or_default()
(the state implements Default).
- The env-serialization lock is a tokio mutex now: each test holds the
  guard across its view-run awaits (a std guard across an await is a
  clippy error, and the tokio lock is the right tool for an async test).
- The mock command loop is a while-let over read_line (while_let_loop).
- The module doc backticks PRIME_AGENT_CODING_AGENT_DIR (doc_markdown).

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/pa-tui/src/incident_notices.rs
…size+mtime

An append changes both size and mtime, so the Windows identity flipped every
poll - re-tailing consumed bytes into phantom update restarts (Cursor Bugbot:
Windows log identity unstable on append). Per the platform identity precedent
(pa_types::platform::identity's None), the non-Unix arm now has no identity:
appends stay same-generation and a rotation still re-tails through the
offset-past-size check (the daemon recreates the live log empty), at the cost
of the .old bridge on that platform. Unix behavior is unchanged.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d455301. Configure here.

Comment thread crates/pa-cli/src/incident/mod.rs
… torn tail

scan_incident_log_files read with read_to_string, which fails the whole
file on any invalid UTF-8 - a torn multi-byte write at the live log's tail
(costing every structured line around it, exactly when the operator runs
the CLI during an active incident). Node's readFile + toString keeps the
tear as replacement characters - the agents-view reader's own lossy rule -
so the scan now reads bytes and decodes with from_utf8_lossy: complete
lines still classify, the torn line fails the parse like any non-line, and
only a read error skips the file whole. Regression test: a third line cut
two bytes into its three-byte U+26A0 keeps the two complete events
(2 in window, 1 unreadable skipped).
The stderr-forward classifier keys on exactly 12 hex worker id chars
(STDERR_FORWARD), so the 10-char fixture id fell to the generic supervisor
event and the regression test asserted the wrong summary.
@kevinjosethomas
kevinjosethomas merged commit 5dbf6f6 into rust Sep 26, 2026
11 of 12 checks passed
@kevinjosethomas
kevinjosethomas deleted the lane/incident-cli-port branch September 26, 2026 19:45
kevinjosethomas added a commit that referenced this pull request Sep 26, 2026
#2866 incident forensics) — conductor-directed refire head; no code change (zero-conflict fold)
kevinjosethomas added a commit that referenced this pull request Sep 26, 2026
… CLI, #2878 supervisor split 2/8; pa-core session untouched upstream - clean fold)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant