Skip to content

feat: a persistent lifecycle event log for dl and aid - #655

Open
blooop wants to merge 1 commit into
mainfrom
feat/event-log
Open

blooop wants to merge 1 commit into
mainfrom
feat/event-log

Conversation

@blooop

@blooop blooop commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

dl and aid now append one JSON line per lifecycle event to $XDG_STATE_HOME/devlaunch/events.jsonl (~/.local/state/devlaunch/events.jsonl by default).

What it logs. Every line has ts (UTC, ms), ev, ws, repo, branch and host. Then, by kind:

  • launch: cold, seconds (top of the command to the session), stages (the DEVLAUNCH_TIMING stage seconds, null when timing is off), herdr, herdr_pane
  • session_end: seconds, exit
  • stop, kill, remove: nothing more, and only when the verb succeeded
  • prune: removed, bytes_freed (null if a clone could not be measured)
  • aid_start: agent, seconds (aid start to the hand-off to dl), stages (pick, pr_lookup, prompt, boot_wait, each present only if the line took that step), resume
  • aid_end: seconds, exit

Lines are flat apart from stages and self-describing through ev, so a reader that unions by field name needs no change when a field is added.

How to turn it off. DEVLAUNCH_EVENTS=0 (or false, no). DEVLAUNCH_EVENTS_PATH moves the file. The sink swallows every IO error and writes each line with a single O_APPEND write, so a log that cannot be written changes nothing about the command.

Why. It feeds a weekly usage review that joins it with herdr's and Claude Code's logs on DEVLAUNCH_WORKSPACE_ID and HERDR_PANE_ID.

Where the hooks are. launch and session_end are written in core's Launch::attach, around the session, because the facts (cold or warm, the resolved id) are there. stop, kill, remove and prune are written by the dl binary after the verb succeeds. A removal reads its record before the delete takes it. dl <ws> up and code hand over no session and write no line. The new module is devlaunch-core/src/events.rs, switched like timing.rs.

Tests: unit tests for the line shape, the switch, the path and an unwritable path. Binary-boundary tests for each kind in dl/tests/launch.rs, dl/tests/lifecycle.rs and aid/tests/rewrite.rs. test/conftest.py now scopes XDG_STATE_HOME, so the Python suite stays out of a developer's real log. public-api.rest.txt is regenerated for the new events module.

🤖 Generated with Claude Code

Summary by Sourcery

Add persistent, fault-tolerant lifecycle event logging across dl and aid.

New Features:

  • Add a persistent JSONL lifecycle event log covering dl launches and sessions, workspace lifecycle operations, pruning, and aid runs.
  • Support disabling the event log and redirecting it to a custom path through environment variables while keeping logging failures non-fatal.

Enhancements:

  • Capture workspace identity, host, timing, session status, agent, and operation-specific metadata for usage analysis.
  • Keep concurrent event appends line-oriented and isolate test logs from developers’ real state directories.

Documentation:

  • Document the lifecycle event schema, configuration switches, event ordering, and logging behavior.

Tests:

  • Add unit, binary-boundary, and Python test coverage for event shape, ordering, lifecycle outcomes, configuration, custom paths, and unwritable logs.

Chores:

  • Regenerate the core public API listing for the new events module.

Every launch, session end, stop, kill, rm and --prune appends one JSON line
to $XDG_STATE_HOME/devlaunch/events.jsonl (~/.local/state/devlaunch by
default), and aid adds aid_start and aid_end around the dl::run it hands
its line to. Each line carries ts, ev, ws, repo, branch and host, and the
facts of its kind: cold, seconds, stages, herdr and herdr_pane for a
launch; seconds and exit for a session end; removed and bytes_freed for a
prune; agent, seconds, stages and resume for aid_start.

On by default. DEVLAUNCH_EVENTS=0 turns it off and DEVLAUNCH_EVENTS_PATH
moves it. The sink swallows every IO error and writes each line with one
append, so a log that cannot be written changes nothing about a command.

The launch facts are core's, so the launch and session_end lines are
written in Launch::attach, around the session. The lifecycle verbs are
logged by the dl binary once each verb has succeeded; a removal reads its
record before the delete takes it. The Python suite now scopes
XDG_STATE_HOME so its runs stay out of the developer's log.

@sourcery-ai sourcery-ai 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.

Sorry @blooop, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 2 days and 1 hour by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Sep 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR introduces an enabled-by-default, persistent JSONL lifecycle log shared by dl and aid, implemented through a best-effort process-global core sink and instrumentation at session, lifecycle-verb, and aid handoff boundaries; it also documents the schema and adds broad coverage with isolated test state.

Sequence diagram for aid handoff and lifecycle logging

sequenceDiagram
    participant User
    participant Aid as aid
    participant Events as events sink
    participant Dl as dl
    participant Session as workspace session

    User->>Aid: run aid
    Aid->>Aid: pick_workspace
    Aid->>Aid: pull_request_spec
    Aid->>Aid: collect_prompt
    Aid->>Aid: boot.finish
    Aid->>Events: record AidStart
    Aid->>Dl: run(dl_args)
    Dl->>Events: record launch
    Dl->>Session: attach_workspace
    Session-->>Dl: exit status
    Dl->>Events: record session_end
    Dl-->>Aid: return exit code
    Aid->>Events: record AidEnd
Loading

Sequence diagram for workspace lifecycle event logging

sequenceDiagram
    participant User
    participant Dl as dl
    participant Events as events sink
    participant Devpod
    participant Records

    User->>Dl: stop, kill, remove, or --prune
    alt stop
        Dl->>Devpod: stop workspace
        Devpod-->>Dl: success
        Dl->>Records: read workspace subject
        Dl->>Events: record stop
    else kill or remove
        Dl->>Records: read subject before deletion
        Dl->>Devpod: kill or remove workspace
        Devpod-->>Dl: success
        Dl->>Events: record kill or remove
    else prune
        Dl->>Dl: prune clone directories
        Dl->>Events: record prune
    end
Loading

File-Level Changes

Change Details Files
Add a process-global, best-effort JSONL event sink with configurable enablement and storage.
  • Resolve the default XDG state path and optional override path.
  • Serialize common workspace/host/timestamp fields plus event-specific payloads.
  • Append each event with one O_APPEND write while swallowing filesystem errors.
  • Expose command-scoped initialization and timing for dl and aid.
rust/devlaunch-core/src/events.rs
rust/devlaunch-core/src/domain/xdg.rs
rust/devlaunch-core/src/lib.rs
rust/dl/src/lib.rs
rust/Cargo.toml
rust/devlaunch-core/public-api.rest.txt
Instrument core session handling with launch and session completion events.
  • Capture cold versus warm startup, elapsed time, timing stages, Herdr metadata, and resolved workspace identity.
  • Write launch before terminal handoff and session_end only after a successful attached session returns.
  • Preserve event subject metadata before records can be removed.
rust/devlaunch-core/src/flows/launch.rs
rust/devlaunch-core/src/timing.rs
Instrument dl lifecycle verbs with success-only operational events.
  • Log successful stop, kill, remove, and prune operations.
  • Distinguish kill from remove and include prune counts and measured freed bytes.
  • Avoid logging refused or declined operations and read removal metadata before deletion.
rust/dl/src/commands.rs
Instrument aid runs and correlate their timing with dl's events.
  • Measure aid-specific picker, PR lookup, prompt, and boot-wait stages.
  • Write aid_start before handing off to dl and aid_end with the final exit status.
  • Include agent, resume status, and workspace subject information while leaving invalid aid invocations unlogged.
rust/aid/src/main.rs
rust/aid/tests/rewrite.rs
Add coverage and documentation for the event-log contract and test isolation.
  • Document event schemas, ordering, configuration, failure behavior, and non-session commands.
  • Add unit and binary-boundary tests for event shapes, switches, paths, failures, and successful lifecycle verbs.
  • Scope Python tests to an isolated XDG state directory and update the changelog and environment-variable reference.
rust/devlaunch-core/src/events.rs
rust/dl/tests/launch.rs
rust/dl/tests/lifecycle.rs
rust/aid/tests/rewrite.rs
test/conftest.py
docs/cli.md
README.md
CHANGELOG.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov

codecov Bot commented Sep 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.18970% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.82%. Comparing base (ca1129c) to head (c2aa49b).

Files with missing lines Patch % Lines
rust/devlaunch-core/src/events.rs 98.49% 5 Missing ⚠️
rust/aid/src/main.rs 90.00% 4 Missing ⚠️
rust/devlaunch-core/src/flows/launch.rs 93.75% 1 Missing ⚠️
rust/dl/src/commands.rs 94.44% 1 Missing ⚠️
rust/dl/src/lib.rs 83.33% 1 Missing ⚠️
Additional details and impacted files
Flag Coverage Δ
python 42.98% <ø> (ø)
rust 95.05% <97.18%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
shipped code (rust) 95.05% <97.18%> (+0.02%) ⬆️
harness and tooling (python) 42.98% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

blooop added a commit to blooop/dotfiles that referenced this pull request Sep 29, 2026
The weekly review saw tokens and words but not time. Three logs now add
it: a Claude hook (telemetry.sh) on every lifecycle event, a herdr
plugin that logs focus and agent-status events, and devlaunch's event
log (blooop/devlaunch#655). All three are flat JSONL that DuckDB reads
in place, so a new goal is a view in sql/sources.sql and a metric file
in sql/metrics/, not a code change. usage_stats.py stays the transcript
ingest and the runner.

Fixes become experiments in ~/.claude/usage-reviews/experiments.jsonl,
each with a metric, a baseline and a check date
(references/methodology.md). A daily digest prints in the first shell
of the day (usage-digest), the weekly review judges experiments that
are due, and a monthly retro removes what did not move its metric.
usage-db opens the logs for ad-hoc questions.

Also drops the two zellij-attention hooks; zellij is gone.

This branch has not been deployed

No deployments
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