Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions docs/pr-audits/pr-6-fix-activity-state-20260203.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# PR Audit: fix(activity): stabilize codex and opencode states (PR #6)

Branch: fix/activity-state
Audit Branch: audit/pr-6-fix-activity-state-20260203
Base: main

## Scope
- Full diff audit for PR #6 as fetched locally.
- Goals: reduce code size, time complexity, memory use, and document tradeoffs.

## Summary Of Findings
- No functional regressions identified in the diff as applied to activity state.
- Primary opportunities: remove duplicate status parsing in OpenCode session handling and avoid repeated regex evaluations.
- Implemented: consolidated status parsing via `deriveStatusFlags` to reduce code size and repeated regex checks.

## Changes Applied In Audit Branch
- src/scan.ts: replace repeated inline status regex checks with `deriveStatusFlags` helper.

## Detailed Audit Notes

### src/scan.ts
- Lines around status authority checks: duplicated status parsing existed for both main session handling and subagent handling.
- Risk: inconsistent future updates if new status terms are added.
- Change: centralize parsing to reduce code and maintain a single regex set.
- Complexity: O(1) per call; reduces constant factors by reusing helper.
- Memory: negligible; helper returns small object and reduces duplicated regex literals.
- Tradeoff: introduces small helper allocation; clarity and DRY preferred.

- Subagent status parsing: same regex set used in subagent block.
- Change: use `deriveStatusFlags` for consistent behavior.
- Tradeoff: minor object creation vs repeated regex checks; acceptable for clarity and maintainability.

## Recommendations Not Implemented
- Consider turning status regexes into precompiled constants to avoid re-allocations if this hot path becomes a bottleneck.
- Consider caching per-session status flags if status strings do not change frequently (requires careful invalidation).

## Verification
- Not run (not requested).
40 changes: 28 additions & 12 deletions src/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,21 @@ function maxMs(...values: Array<number | undefined>): number | undefined {
return max;
}

function deriveStatusFlags(value: unknown): {
status?: string;
isError: boolean;
isIdle: boolean;
isActive: boolean;
} {
const status = typeof value === "string" ? value.toLowerCase() : undefined;
return {
status,
isError: !!status && /error|failed|failure/.test(status),
isIdle: !!status && /idle|stopped|paused/.test(status),
isActive: !!status && /running|active|processing|busy|retry/.test(status),
};
}

function deriveTitle(
doing: string | undefined,
repo: string | undefined,
Expand Down Expand Up @@ -1830,15 +1845,15 @@ export async function scanCodexProcesses(options: ScanOptions = {}): Promise<Sna
statusAuthorityFresh && typeof statusAuthority === "string"
? statusAuthority.toLowerCase()
: undefined;
const statusAuthorityIsIdle =
!!statusAuthorityLower && /idle|stopped|paused/.test(statusAuthorityLower);
const statusAuthorityIsBusy = !!statusAuthorityLower && !statusAuthorityIsIdle;

const statusAuthorityFlags = deriveStatusFlags(statusAuthorityLower);
const statusAuthorityIsIdle = statusAuthorityFlags.isIdle;
const statusAuthorityIsBusy = !!statusAuthorityFlags.status && !statusAuthorityIsIdle;
const statusRaw = typeof session?.status === "string" ? session.status : undefined;
const status = statusAuthorityLower ?? statusRaw?.toLowerCase();
const statusIsError = !!status && /error|failed|failure/.test(status);
const statusIsIdle = !!status && /idle|stopped|paused/.test(status);
const statusIsActive = !!status && /running|active|processing|busy|retry/.test(status);
const statusFlags = deriveStatusFlags(statusAuthorityLower ?? statusRaw);
const status = statusFlags.status;
const statusIsError = statusFlags.isError;
const statusIsIdle = statusFlags.isIdle;
const statusIsActive = statusFlags.isActive;
let hasError = statusIsError;
const model = typeof session?.model === "string" ? session.model : undefined;
const includeOpenCode = shouldIncludeOpenCodeProcess({
Expand Down Expand Up @@ -2178,10 +2193,11 @@ export async function scanCodexProcesses(options: ScanOptions = {}): Promise<Sna
? (session as { status?: string }).status
: undefined;
const statusCandidate = eventActivity?.lastStatus ?? statusRaw;
const status = typeof statusCandidate === "string" ? statusCandidate.toLowerCase() : undefined;
const statusIsError = !!status && /error|failed|failure/.test(status);
const statusIsIdle = !!status && /idle|stopped|paused/.test(status);
const statusIsActive = !!status && /running|active|processing|busy|retry/.test(status);
const statusFlags = deriveStatusFlags(statusCandidate);
const status = statusFlags.status;
const statusIsError = statusFlags.isError;
const statusIsIdle = statusFlags.isIdle;
const statusIsActive = statusFlags.isActive;
const sessionUpdatedAt = opencodeSessionTimestamp(session);
const lastActivityAt = maxMs(
toMs(eventActivity?.lastActivityAt),
Expand Down
Loading