Skip to content

perf(CC-364): single streaming jq pass for pmctl trace tail - #546

Merged
screenleon merged 4 commits into
mainfrom
perf/cc-364-trace-tail-single-jq-pass
Aug 27, 2026
Merged

perf(CC-364): single streaming jq pass for pmctl trace tail#546
screenleon merged 4 commits into
mainfrom
perf/cc-364-trace-tail-single-jq-pass

Conversation

@screenleon

@screenleon screenleon commented Aug 27, 2026

Copy link
Copy Markdown
Owner

CC-364 — pmctl trace tail single streaming jq pass

Problem

pmctl trace tail spawned two jq processes per event in the scan phase
(jq -c type check + jq -r @tsv field extract) plus one more per row in the
human emitter. --all was O(n) with a high constant — ~20s for 338 events.

Change

Scan phase is now one jq -R streaming pass over the concatenated
archive + active stream. It classifies each line (malformed / filtered-out /
kept) and emits kept rows as <ts>\t<line_no>\t<compact-json>, using jq's
cumulative input_line_number as the global read-order tiebreaker for events
sharing a timestamp. Both emit helpers stream through a single jq via cut -f3.

Measured: 400 events --all --json ~24s → 0.2s (~100x); jq invocation
count is now fixed (1 for --json, 2 for human) regardless of event count.

Behavior parity (unchanged)

--kind / --task / --subject / --id filters, inclusive lexicographic
--since/--until window (empty ts excluded when bounded), malformed-row
tolerance + trace: skipped N malformed row(s) warning, archive/active
chronological merge, -n / --limit / --limit 0 / --all semantics,
compact-JSON byte identity. Drops the five module-global _PMCTL_TRACE_*
vars and three now-dead scan helpers.

Reuse note

pmctl-run-stats.sh (CC-358) has the same archive+active events.jsonl scan
shape with a per-line jq spawn. Deliberately not refactored here — different
jq program + shell-side aggregation, out of CC-364 scope, and a shared
"event source" primitive would be a single-consumer abstraction until run-stats
is also converted. Left a pointer comment; no ticket exists yet for that
follow-up.

Tests

  • case_trace_large_partition_streaming — 240 active events across 3 disjoint
    timestamp bands + 2 malformed rows; asserts count, non-decreasing merge
    order, skip warning. Active-only so it always executes.
  • case_trace_tail_single_jq_pass — counting jq shim on PATH; asserts the
    invocation tally is equal (and non-zero) for a 20-event and a 200-event run,
    i.e. O(1) in event count. Fault-sensitive: a per-event regression makes the
    200-event tally ~10x the 20-event one.
  • test-pmctl-trace.sh 14 passed; full suite run-all-tests.sh 105 passed,
    0 failed, 0 skipped
    .

PR-gate

3 rounds (round 1 NO-GO on two test-only qa-tester findings, rounds 2–3 GO;
rounds 2–3 sequential). Final round: critic / qa-tester / architecture /
security all approve, zero findings.

BACKLOG

CC-364 index row + body section marked ✅ done with a Closure 2026-08-27 (pr:#546) note in this PR (docs(BACKLOG): mark CC-364 done). validate.sh,
test-pmctl-backlog, test-archive-closed-backlog,
test-schema-task-mirrors-backlog all pass.

🤖 Generated with Claude Code

screenleon and others added 4 commits August 27, 2026 13:13
pmctl trace tail spawned two jq processes per event during the scan phase
plus one more per row in the human emitter, making --all O(n) with a high
constant (~20s / 338 events). Rework the scan as one jq -R streaming pass
over the concatenated archive+active stream: it classifies each line
(malformed / filtered-out / kept) and emits kept rows as
"<ts>\t<line_no>\t<compact-json>", using jq's cumulative input_line_number
as the global read-order tiebreaker for equal timestamps. Both emit
helpers now stream through a single jq via cut -f3.

Behavior is unchanged: same filters, inclusive lexicographic time window,
malformed-row tolerance + "skipped N" warning, archive/active chronological
merge, limit/--all semantics, compact-JSON byte identity. Drops the five
module-global _PMCTL_TRACE_* vars and three now-dead scan helpers.

New regression: 120+120 archive/active events with interleaved bands and
two malformed rows, asserting count, non-decreasing merge order and the
skip warning at a scale above the other cases (awk-generated fixtures).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFPwSUfmLGh6KJLYFBArTS
pr-gate (qa-tester, 2x high/block) on the first draft of
case_trace_large_partition_streaming:

- F001: the "gzip unavailable -> pass; return 0" guard let a runner
  without gzip record the case as verified without running its archive
  coverage. Reworked the case to an active-file-only partition (240
  events across three disjoint timestamp bands + two malformed rows),
  so it always executes; archive+active merge correctness stays covered
  by case_trace_active_archive_merge.
- F002: added the harness-required Behavior/Steps docstring above the
  case; observable assertions unchanged.

Focused: test-pmctl-trace.sh (13 passed), test-lint-test-docstrings.sh,
shellcheck clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFPwSUfmLGh6KJLYFBArTS
pr-gate round 2 was GO with one critic advisory (critic-F001, low): the
scaled regression proves output correctness but nothing fails if per-event
jq spawning returns. Add case_trace_tail_single_jq_pass: a counting jq
shim on PATH tallies invocations across a 20-event and a 200-event run of
`trace tail --all --json`; the test asserts the tallies are equal and
non-zero (O(1) in event count). Streaming impl invokes jq once per run
regardless of size; a per-event regression would make the 200-event tally
~10x the 20-event one.

test-pmctl-trace.sh: 14 passed. shellcheck clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFPwSUfmLGh6KJLYFBArTS
Index row -> done, body section gets a Closure 2026-08-27 (pr:#546) note
and perf/parity evidence summary. validate.sh, test-pmctl-backlog,
test-archive-closed-backlog, test-schema-task-mirrors-backlog all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFPwSUfmLGh6KJLYFBArTS
@screenleon
screenleon merged commit 6f048e6 into main Aug 27, 2026
66 checks passed
@screenleon
screenleon deleted the perf/cc-364-trace-tail-single-jq-pass branch August 27, 2026 06:07
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