Skip to content

refactor(tracker): library API hardening for v1.0#113

Open
clintecker wants to merge 2 commits intomainfrom
feat/library-api-v1
Open

refactor(tracker): library API hardening for v1.0#113
clintecker wants to merge 2 commits intomainfrom
feat/library-api-v1

Conversation

@clintecker
Copy link
Copy Markdown
Collaborator

@clintecker clintecker commented Apr 17, 2026

Summary

Batches six pre-1.0 library-API issues flagged in the expert panel review of PR #101 into one breaking change. All of them touch the new v0.18.0 library surface (tracker.Doctor, tracker.Diagnose, tracker.Audit, tracker.Simulate, NDJSONWriter), so bundling them means one semver bump instead of six.

Closes #102, #103, #104, #105, #106, #109.

What's in

Test plan

  • go build ./... passes
  • go test ./... -short passes (all 17 packages)
  • go test ./... full suite passes
  • go test -race -run TestNDJSON passes
  • go vet ./... clean
  • dippin doctor on the three core pipelines → A / 100
  • Smoke tests: tracker doctor, tracker simulate ..., tracker list still produce correct output
  • New unit tests added for: sanitizeProviderError (5 cases), NDJSONWriter.Write error propagation, panic recovery in all three handler factories, LogWriter parameter redirecting warnings in ListRuns

Breaking changes (library only)

Callers of the following signatures need updates:

// Before                                     // After
tracker.Doctor(cfg)                           tracker.Doctor(ctx, cfg, opts...)
tracker.Diagnose(runDir)                      tracker.Diagnose(ctx, runDir, opts...)
tracker.DiagnoseMostRecent(workdir)           tracker.DiagnoseMostRecent(ctx, workdir, opts...)
tracker.Audit(runDir)                         tracker.Audit(ctx, runDir, opts...)
tracker.Simulate(source)                      tracker.Simulate(ctx, source)

type NDJSONEvent struct{...}                  type StreamEvent struct{...}  // rename only
func (*NDJSONWriter) Write(e NDJSONEvent)     func (*NDJSONWriter) Write(e StreamEvent) error

tracker.DoctorConfig{TrackerVersion: v, ...}  tracker.Doctor(ctx, cfg, tracker.WithVersionInfo(v, c))

Typed CheckStatus / SuggestionKind are source-compatible with untyped string-literal comparisons (status == "ok" still works).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Breaking Changes

    • Core public APIs now take an explicit context and accept optional config/options; some public fields were replaced by a functional option for version info. Typed enums replace previously untyped status/kind strings.
  • New Features

    • Configurable LogWriter for non-fatal warnings; writer operations now surface write errors.
  • Performance

    • Activity logs are parsed streamingly (line-by-line) to reduce memory use.
  • Bug Fixes

    • Provider error messages are redacted; handler panics are recovered.

Batches six related pre-1.0 fixes flagged in the expert panel review
of PR #101 (#102, #103, #104, #105, #106, #109). All changes touch
the v0.18.0 library surface and are breaking, so ship together:

- Thread context.Context through Doctor, Diagnose, DiagnoseMostRecent,
  Audit, and Simulate. Provider probes and binary version lookups
  honor ctx; getBinaryVersion now uses exec.CommandContext with a
  5-second timeout to match getDippinVersion.
- Introduce typed CheckStatus and SuggestionKind so consumers can
  switch-exhaust. Constant values are unchanged.
- NDJSONWriter.Write now returns error. First write failure is still
  logged once to stderr; subsequent failures surface via return value.
- Rename NDJSONEvent -> StreamEvent (wire format unchanged).
- Move DoctorConfig.TrackerVersion/TrackerCommit off the config struct
  behind a WithVersionInfo functional option.
- Add LogWriter to DoctorConfig/DiagnoseConfig/AuditConfig. CLI sets
  it to io.Discard so users stop seeing stray library warnings from
  audit/list/diagnose on os.Stderr.
- sanitizeProviderError strips API keys and bearer tokens from
  provider error bodies before they land in CheckDetail.Message.
- Panic-recover the NDJSON handler closures (pipeline, agent, LLM
  trace) so a misbehaving writer cannot crash the caller goroutine.
- Stream activity.jsonl in Diagnose via bufio.Scanner (1 MB buffer
  limit), matching LoadActivityLog and avoiding memory spikes on
  large runs.
- Rename internal *Lib/*NF/*ForDiagnose helpers to plain names.
- CHANGELOG notes deterministic diagnose suggestion ordering (already
  alphabetical post-#101, documented as a visible change).

Closes #102, #103, #104, #105, #106, #109.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 17, 2026

Walkthrough

This PR applies coordinated API and implementation changes across the tracker package: adds context parameters to public entry points, introduces typed enums for statuses/kinds, renames NDJSON/wire types and makes writer writes return errors, replaces DoctorConfig version fields with a functional option, streams activity parsing, adds configurable LogWriter for warnings, and sanitizes provider error messages.

Changes

Cohort / File(s) Summary
Git / Docs
/.gitignore, CHANGELOG.md, README.md
Ignore runs/; document and example updates for new ctx-first signatures, typed enums, functional options, and rename from NDJSONEventStreamEvent.
CLI wrappers
cmd/tracker/audit.go, cmd/tracker/diagnose.go, cmd/tracker/doctor.go, cmd/tracker/simulate.go
Call updated tracker APIs with context.Background() and pass Diagnose/Audit configs using LogWriter: io.Discard; use WithVersionInfo option for doctor CLI.
Audit surface & tests
tracker_audit.go, tracker_audit_test.go
Add exported AuditConfig{LogWriter io.Writer}; change Audit and ListRuns to accept context.Context and variadic AuditConfig; route warnings to provided writer; add test verifying LogWriter silences warnings.
Diagnose surface & tests
tracker_diagnose.go, tracker_diagnose_test.go
Add DiagnoseConfig{LogWriter io.Writer} and SuggestionKind typed string; change Diagnose/DiagnoseMostRecent to ctx-first with variadic config; stream-parse activity.jsonl via bufio.Scanner; add cancellation propagation test.
Doctor surface & tests
tracker_doctor.go, tracker_doctor_unix.go, tracker_doctor_windows.go, tracker_doctor_test.go
Change Doctor to Doctor(ctx, cfg, opts...); add DoctorOption and WithVersionInfo; introduce CheckStatus typed constants and update assignments; add provider error sanitization; rename helper functions; update tests.
Event streaming & tests
tracker_events.go, tracker_events_test.go
Rename NDJSONEventStreamEvent; change writer signature to Write(StreamEvent) error; add per-writer panic recovery/suppression; update tests for error and panic behaviors.
Simulate & tests
tracker_simulate.go, tracker_simulate_test.go
Change Simulate to accept context.Context as first arg (currently unused) and update tests to pass context.
Activity helper
tracker_activity.go
Rename parseActivityTimestampLibparseActivityTimestamp (remove Lib suffix).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #103: Shares objectives — introduces streaming activity.jsonl parsing and context-aware timeouts for Diagnose/Doctor probes.
  • #110: Matches the PR's breaking API changes (signatures, type renames, options) and documentation/migration needs.

Possibly related PRs

  • #101: Continues/refines CLI↔library parity and API shape changes introduced there.
  • #83: Overlaps with doctor CLI flow and DoctorConfig/doctor command adjustments.
  • #68: Related changes around diagnose/audit activity parsing and CLI call-sites.

Poem

🐰 I hopped through code with nimble paws,
Context in hand to mind the laws,
StreamEvent whispers, no panics loud,
Enums stand proud and options bow.
A tidy patch — the rabbit bows. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main change: a refactoring of the tracker library API in preparation for v1.0 with hardening improvements.
Linked Issues check ✅ Passed The PR addresses all six objectives from issue #102: context.Context addition [#102], typed string types CheckStatus and SuggestionKind [#102], NDJSONWriter.Write error return [#102], removal of internal helper suffixes [#102], DoctorConfig version field removal via WithVersionInfo [#102], and NDJSONEvent → StreamEvent rename [#102].
Out of Scope Changes check ✅ Passed All changes align with the PR objectives: context.Context threading, type strengthening, NDJSONWriter error handling, helper function renaming, LogWriter config addition, and panic recovery in handlers. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/library-api-v1

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b333d15fa8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tracker_diagnose.go Outdated
Comment on lines +220 to +222
if ctx.Err() != nil {
break
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Return cancellation error when Diagnose context is done

Breaking out of the scan loop on ctx.Err() returns a partial DiagnoseReport with nil error, so callers using deadlines/cancellation cannot tell the analysis was truncated. This makes downstream automation treat incomplete diagnostics as authoritative whenever the context is canceled mid-parse. Diagnose should propagate ctx.Err() (or wrap it) instead of silently succeeding with partial results.

Useful? React with 👍 / 👎.

Comment thread tracker_diagnose.go Outdated
Comment on lines 241 to 242
applyRetryAnalysis(failures, failSignatures)
return halt
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle scanner errors before returning Diagnose activity data

enrichFromActivity never checks scanner.Err(), so read failures (including bufio.ErrTooLong once a JSONL line exceeds the 1 MB buffer) are silently ignored and the function returns incomplete failure/retry/budget analysis as if successful. Because this path replaced full-file parsing, large or corrupted activity.jsonl inputs now degrade results without any signal.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tracker_diagnose.go (1)

204-243: ⚠️ Potential issue | 🟡 Minor

Scanner error is not checked after the loop.

The bufio.Scanner can fail for reasons other than EOF (e.g., line exceeds buffer size despite the 1MB limit, or I/O error). The code correctly sets up the scanner with a larger buffer but doesn't check scanner.Err() after the loop exits. While enrichFromActivity returns only the BudgetHalt struct (not an error), silently ignoring scanner errors could mask truncated reads.

Consider logging scanner errors to logW if one is available in scope, or at minimum documenting that parse errors are ignored.

🔧 Proposed fix to log scanner errors
 	for scanner.Scan() {
 		if ctx.Err() != nil {
 			break
 		}
 		line := strings.TrimSpace(scanner.Text())
 		if line == "" {
 			continue
 		}
 		var entry diagnoseEntry
 		if err := json.Unmarshal([]byte(line), &entry); err != nil {
 			continue
 		}
 		if entry.Type == "budget_exceeded" {
 			halt = &BudgetHalt{
 				TotalTokens:   entry.TotalTokens,
 				TotalCostUSD:  entry.TotalCostUSD,
 				WallElapsedMs: entry.WallElapsedMs,
 				Message:       entry.Message,
 			}
 		}
 		enrichFromEntry(entry, failures, stageStarts, failSignatures)
 	}
+	// Scanner errors (I/O, buffer overflow) are non-fatal for diagnosis;
+	// partial data is still useful. Could log to a writer if needed.
+	_ = scanner.Err()
 	applyRetryAnalysis(failures, failSignatures)
 	return halt
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tracker_diagnose.go` around lines 204 - 243, enrichFromActivity uses
bufio.Scanner but never checks scanner.Err() after the Scan loop, which can
silently drop I/O or buffer errors; after the for scanner.Scan() loop in
enrichFromActivity, call err := scanner.Err() and if err != nil log the error
(prefer using existing logger variable logW if in scope, otherwise the package
logger or fmt) including context like runDir and the error; do not change the
function signature—just add the post-loop check and a descriptive log line
referencing scanner, runDir, and the error.
🧹 Nitpick comments (1)
tracker_events.go (1)

148-156: Package-level ndjsonPanicOnce suppresses panics across all writer instances.

The ndjsonPanicOnce is a package-level sync.Once, meaning that if one NDJSONWriter instance recovers from a panic, all subsequent panics from any NDJSONWriter instance in the process will be silently suppressed without logging. This could mask unrelated issues when multiple writers are used (e.g., different output streams for different pipeline runs).

Consider making this per-instance on NDJSONWriter:

Suggested change
 type NDJSONWriter struct {
 	mu        sync.Mutex
 	w         io.Writer
 	errOnce   sync.Once
+	panicOnce sync.Once
 }
 
-var ndjsonPanicOnce sync.Once
-
-func recoverNDJSONPanic(source string) {
+func (s *NDJSONWriter) recoverPanic(source string) {
 	if r := recover(); r != nil {
-		ndjsonPanicOnce.Do(func() {
+		s.panicOnce.Do(func() {
 			fmt.Fprintf(os.Stderr, "tracker: NDJSON %s handler recovered from panic: %v (further panics suppressed)\n", source, r)
 		})
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tracker_events.go` around lines 148 - 156, The package-level ndjsonPanicOnce
causes panic-suppression across all NDJSONWriter instances; change this to a
per-instance sync.Once field on NDJSONWriter (e.g., add a field like panicOnce
sync.Once), convert recoverNDJSONPanic into a method on *NDJSONWriter that calls
the instance's panicOnce.Do and prints the same message (or accept the source
string), and update all call sites that currently call recoverNDJSONPanic(...)
to call the new method (writer.recoverNDJSONPanic(...)). This ensures each
NDJSONWriter tracks its own suppression state and prevents unrelated writers
from being muted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@README.md`:
- Line 591: The README line incorrectly implies all four APIs accept optional
config structs with LogWriter; update the wording to state that tracker.Audit,
tracker.Doctor and the Diagnose API (AuditConfig, DoctorConfig, DiagnoseConfig)
accept an optional config struct with a LogWriter for non‑fatal parse warnings
(set to io.Discard to silence), while tracker.Simulate does not use those config
structs and does not accept a LogWriter—adjust the sentence to explicitly
exclude tracker.Simulate and name the exact structs (AuditConfig,
DiagnoseConfig, DoctorConfig) and LogWriter to avoid confusion.

In `@tracker_audit.go`:
- Around line 85-89: The nil-check in Audit(ctx context.Context, runDir string,
opts ...AuditConfig) is ineffectual because ctx is reassigned but never used; to
silence the linter and document intent, keep the nil-check but add a deliberate
no-op usage like `_ = ctx` after the check (or alternatively remove the
nil-check entirely if you prefer), referencing the Audit function and the
existing call to firstAuditConfig(opts) so reviewers can find the spot to
modify.

In `@tracker_doctor.go`:
- Around line 41-50: DoctorConfig.LogWriter is declared but never used; add a
helper similar to logWriterOrDiscard() and thread its result through the Doctor
flow and any check* functions that emit non-fatal parse warnings so those checks
write to LogWriter instead of being silent. Specifically, implement a
logWriterOrDiscard() helper (as used in tracker_audit.go and
tracker_diagnose.go), call it at the start of Doctor (or where DoctorConfig is
consumed), pass the returned io.Writer into check functions invoked by Doctor
(e.g., any check* functions in this file), and replace direct writes to
stderr/discard with fmt.Fprintf to the provided writer; alternatively remove
DoctorConfig.LogWriter if you opt not to surface warnings.

---

Outside diff comments:
In `@tracker_diagnose.go`:
- Around line 204-243: enrichFromActivity uses bufio.Scanner but never checks
scanner.Err() after the Scan loop, which can silently drop I/O or buffer errors;
after the for scanner.Scan() loop in enrichFromActivity, call err :=
scanner.Err() and if err != nil log the error (prefer using existing logger
variable logW if in scope, otherwise the package logger or fmt) including
context like runDir and the error; do not change the function signature—just add
the post-loop check and a descriptive log line referencing scanner, runDir, and
the error.

---

Nitpick comments:
In `@tracker_events.go`:
- Around line 148-156: The package-level ndjsonPanicOnce causes
panic-suppression across all NDJSONWriter instances; change this to a
per-instance sync.Once field on NDJSONWriter (e.g., add a field like panicOnce
sync.Once), convert recoverNDJSONPanic into a method on *NDJSONWriter that calls
the instance's panicOnce.Do and prints the same message (or accept the source
string), and update all call sites that currently call recoverNDJSONPanic(...)
to call the new method (writer.recoverNDJSONPanic(...)). This ensures each
NDJSONWriter tracks its own suppression state and prevents unrelated writers
from being muted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 69bf4bea-3dee-4100-be47-c0fc75718dcb

📥 Commits

Reviewing files that changed from the base of the PR and between 9e5f1b7 and b333d15.

📒 Files selected for processing (20)
  • .gitignore
  • CHANGELOG.md
  • README.md
  • cmd/tracker/audit.go
  • cmd/tracker/diagnose.go
  • cmd/tracker/doctor.go
  • cmd/tracker/simulate.go
  • tracker_activity.go
  • tracker_audit.go
  • tracker_audit_test.go
  • tracker_diagnose.go
  • tracker_diagnose_test.go
  • tracker_doctor.go
  • tracker_doctor_test.go
  • tracker_doctor_unix.go
  • tracker_doctor_windows.go
  • tracker_events.go
  • tracker_events_test.go
  • tracker_simulate.go
  • tracker_simulate_test.go

Comment thread README.md Outdated
Comment thread tracker_audit.go
Comment thread tracker_doctor.go Outdated
- Diagnose: propagate ctx.Err() instead of silently truncating the report
  on cancellation. Partial reports are never returned as success, so
  deadline-aware callers can distinguish complete from truncated output.
  (Codex P1)
- enrichFromActivity: surface bufio.Scanner errors (1 MB line overflow,
  I/O) instead of dropping them on the floor. Returns the error up to
  Diagnose and logs it via the LogWriter. (Codex P2, CodeRabbit)
- NDJSONWriter: panicOnce is now a per-instance field, not a package-level
  sync.Once. Before this, one misbehaving sink would silence panic
  logging across every NDJSONWriter in the process. Added a test to
  verify per-instance isolation. (CodeRabbit)
- Drop unused DoctorConfig.LogWriter. Doctor doesn't emit stderr warnings
  — the field was added symmetrically with Audit/Diagnose but had no
  consumers. (CodeRabbit)
- Drop ineffectual ctx nil-check in Audit and Simulate. ctx is reserved
  for future extensibility; silence the linter with `_ = ctx` and drop
  the dead nil-guard. (CodeRabbit, golangci-lint)
- README: correct the library-API sentence — Simulate does not take a
  config struct; Doctor uses functional options, not AuditConfig-shaped
  options. (CodeRabbit)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
tracker_simulate.go (1)

57-58: Normalize nil ctx now to lock in API behavior.
Small forward-compat improvement: coalesce nil to context.Background() instead of using _ = ctx, so future cancellation plumbing won’t accidentally break nil-callers.

Suggested diff
func Simulate(ctx context.Context, source string) (*SimulateReport, error) {
-	_ = ctx // reserved for future cancellation plumbing
+	if ctx == nil {
+		ctx = context.Background()
+	}
	format := detectSourceFormat(source)
	graph, err := parsePipelineSource(source, format)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tracker_simulate.go` around lines 57 - 58, The Simulate function currently
ignores the incoming ctx (using "_ = ctx"), which can cause future cancellation
plumbing to break for nil callers; update Simulate (function Simulate in
tracker_simulate.go) to coalesce a nil ctx to context.Background() by checking
if ctx == nil and assigning context.Background() before using ctx so future
cancellation logic works correctly.
tracker_audit.go (1)

17-24: Clarify AuditConfig doc scope to avoid caller ambiguity.

The comment says this config applies to Audit() and ListRuns(), but LogWriter is currently only consumed in ListRuns(). Consider tightening wording so callers don’t expect Audit() warning redirection yet.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tracker_audit.go` around lines 17 - 24, The doc comment for AuditConfig is
misleading: it claims to configure Audit() and ListRuns(), but the LogWriter
field is only used by ListRuns(); either update Audit() to honor LogWriter or
narrow the comment. Change the AuditConfig top-level comment to state it
configures ListRuns() (and/or other functions that actually consume it) and
update the LogWriter field comment to explicitly say "LogWriter is consumed by
ListRuns() only; nil is treated as io.Discard..." so callers won't expect
Audit() to redirect warnings unless you also modify the Audit() implementation
to use AuditConfig.LogWriter.
tracker_events_test.go (1)

392-410: Assert the per-instance stderr side effect here.

This only proves both handlers recover. It does not verify the regression called out in the test name: if panic suppression becomes package-scoped again, this still passes because nothing observes stderr. Capture stderr and assert that each writer emits its own first panic log line.

Possible tightening
 func TestNDJSONWriter_PanicSuppressionIsPerInstance(t *testing.T) {
+	oldStderr := os.Stderr
+	r, wpipe, err := os.Pipe()
+	if err != nil {
+		t.Fatalf("pipe: %v", err)
+	}
+	os.Stderr = wpipe
+	defer func() {
+		os.Stderr = oldStderr
+		_ = r.Close()
+	}()
+
 	defer func() {
 		if r := recover(); r != nil {
 			t.Fatalf("handlers should recover, got: %v", r)
 		}
 	}()
 	w1 := NewNDJSONWriter(&panicWriter{})
 	w2 := NewNDJSONWriter(&panicWriter{})
 	// First panic on w1 — must not consume w2's Once.
 	w1.PipelineHandler().HandlePipelineEvent(pipeline.PipelineEvent{
 		Type:      pipeline.EventPipelineStarted,
 		Timestamp: time.Now(),
 	})
 	// Second panic on w2 — still the first on its own instance.
 	w2.PipelineHandler().HandlePipelineEvent(pipeline.PipelineEvent{
 		Type:      pipeline.EventPipelineStarted,
 		Timestamp: time.Now(),
 	})
+
+	_ = wpipe.Close()
+	out, err := io.ReadAll(r)
+	if err != nil {
+		t.Fatalf("read stderr: %v", err)
+	}
+	if got := len(strings.Split(strings.TrimSpace(string(out)), "\n")); got != 2 {
+		t.Fatalf("got %d panic log lines, want 2", got)
+	}
 }

Also add io and os imports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tracker_events_test.go` around lines 392 - 410, The test
TestNDJSONWriter_PanicSuppressionIsPerInstance must capture and assert stderr
output so it verifies per-instance panic logging: wrap the calls that trigger
panics (use NewNDJSONWriter(&panicWriter{}) and call
w1.PipelineHandler().HandlePipelineEvent(...) and
w2.PipelineHandler().HandlePipelineEvent(...)) while redirecting os.Stderr to a
pipe (using io and os imports), read the captured output, and assert that there
are two separate first-panic log lines (one for w1 and one for w2) indicating
each instance emitted its own panic message; restore stderr after capture.
Ensure assertions reference the instances w1 and w2 and that the captured output
contains distinct panic entries for each.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tracker_audit.go`:
- Around line 17-24: The doc comment for AuditConfig is misleading: it claims to
configure Audit() and ListRuns(), but the LogWriter field is only used by
ListRuns(); either update Audit() to honor LogWriter or narrow the comment.
Change the AuditConfig top-level comment to state it configures ListRuns()
(and/or other functions that actually consume it) and update the LogWriter field
comment to explicitly say "LogWriter is consumed by ListRuns() only; nil is
treated as io.Discard..." so callers won't expect Audit() to redirect warnings
unless you also modify the Audit() implementation to use AuditConfig.LogWriter.

In `@tracker_events_test.go`:
- Around line 392-410: The test TestNDJSONWriter_PanicSuppressionIsPerInstance
must capture and assert stderr output so it verifies per-instance panic logging:
wrap the calls that trigger panics (use NewNDJSONWriter(&panicWriter{}) and call
w1.PipelineHandler().HandlePipelineEvent(...) and
w2.PipelineHandler().HandlePipelineEvent(...)) while redirecting os.Stderr to a
pipe (using io and os imports), read the captured output, and assert that there
are two separate first-panic log lines (one for w1 and one for w2) indicating
each instance emitted its own panic message; restore stderr after capture.
Ensure assertions reference the instances w1 and w2 and that the captured output
contains distinct panic entries for each.

In `@tracker_simulate.go`:
- Around line 57-58: The Simulate function currently ignores the incoming ctx
(using "_ = ctx"), which can cause future cancellation plumbing to break for nil
callers; update Simulate (function Simulate in tracker_simulate.go) to coalesce
a nil ctx to context.Background() by checking if ctx == nil and assigning
context.Background() before using ctx so future cancellation logic works
correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 72db3a01-f4bf-4ac1-95c4-f99b3a5f1fdd

📥 Commits

Reviewing files that changed from the base of the PR and between b333d15 and ec3f68b.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • README.md
  • cmd/tracker/doctor.go
  • tracker_audit.go
  • tracker_diagnose.go
  • tracker_diagnose_test.go
  • tracker_doctor.go
  • tracker_events.go
  • tracker_events_test.go
  • tracker_simulate.go
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • cmd/tracker/doctor.go
  • tracker_diagnose_test.go
  • CHANGELOG.md
  • tracker_diagnose.go
  • tracker_events.go

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.

refactor(tracker): library API polish before v1.0

1 participant