Skip to content

feat: redact records at insert, a JSONL timeline sink, and cursor reads with --follow - #43

Merged
anilcancakir merged 6 commits into
masterfrom
feature/host-timeline
Oct 10, 2026
Merged

anilcancakir merged 6 commits into
masterfrom
feature/host-timeline

Conversation

@anilcancakir

Copy link
Copy Markdown
Member

What

  • Host redactor hook. TelescopeRedaction.redactor is a host-set String Function(String). The store runs it at insert over every string of log, event, exception and HTTP records, payload keys included, before the record reaches a queue or a stream.
    • A throwing redactor drops the record.
    • redacted is read-only and set only by redaction.
    • Event payloads are made JSON-safe at insert.
  • Per-kind capacity. TelescopeStore.setCapacity(int, {TelescopeKind? kind}). LogRecordEntry.atUs puts log lines on the same monotonic clock as events.
  • File sink. TelescopeFileSink writes rotating JSONL, bounded by bytes and file count.
    • It writes only redacted records and counts the rest.
    • It lists and reads earlier launches by name match only (no path built from input, no symlinks).
    • It halts on a write failure instead of looping on its own exception.
  • Reads. ext.telescope.events and ext.telescope.console take since, type and logger and answer a cursor; with since the page is the oldest records after it, so cursor paging is gapless. New ext.telescope.files and ext.telescope.file.
  • CLI. telescope:events and telescope:tail gain --since, --type / --logger, --json and --follow. New telescope:files. followCursor is exported from cli.dart.
  • Fixes.
    • An unknown console level matched everything; it now matches nothing.
    • A non-positive limit threw; it now returns nothing.
    • A non-encodable payload failed the whole events response.
  • Docs. README, llms.txt, CLAUDE.md, doc pages and the skill.

Why

Watchools records its player timeline (events, mpv logs, faults, stats) into telescope so an agent can follow and replay it from the CLI. A stream URL carries the provider login in its path, so every record had to be masked before it is kept, and nothing unmasked may reach the file.

Testing

  • flutter test --exclude-tags=integration --timeout=30s: 497 pass; coverage 97.8%; dart analyze and dart format clean.
  • Driven on macOS through Watchools on the mock and a real panel. Timeline files and CLI output were swept for credentials in literal, percent-encoded and base64 forms, and none were found.

Breaking: TelescopeRedaction.redactHttpRecord now returns a nullable record (null when the redactor rejects it), and the record constructors no longer take redacted.

@codecov

codecov Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.36441% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
lib/src/telescope_file_sink.dart 98.30% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@kodizm

kodizm Bot commented Oct 10, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

Insert-time redaction, the fail-closed sink and the name-matched file reads look sound. One data-loss path in the sink's file naming should be fixed before merge.

Major

lib/src/telescope_file_sink.dart:110 (data-loss): The launch stamp is only accurate to the second, and _rotate opens timeline-<stamp>-1.jsonl with FileMode.write (line 366). If a sink is started again within the same second (for example start() while one is already running, or stop then start), the new sink truncates the first file of the sink it just replaced, and those flushed timeline lines are lost. Two fixes would work: make the stamp finer or add a per-process sequence, or open with FileMode.append and seed _TimelineFile.bytes from the file's existing length. There is no test for a same-second restart.

Minor

lib/src/extensions/register_telescope_extensions.dart:352 (correctness): The cursor is the page's largest atUs and the filter is a strict >. So with since plus limit, a page that ends between two records sharing one atUs (two log lines in the same microsecond) skips the second for good. That contradicts the "skips nothing" promise in the docblock and the CHANGELOG. --follow drops limit after the first read, so this mainly affects manual --since/--limit paging. Lower confidence: it depends on FlutterTimeline.now repeating a value.

Tests

The change is well covered: there are new or extended tests for the store, redaction, the file sink, the handlers, cursor follow and the three commands. The same-second restart above is not covered.

CI

  • Lint & Test: success
  • codecov/patch: failure (97.62% of the diff is covered, below the 97.88% target)
  • Auto-merge low-risk Dependabot PRs: skipped

I read the full source and the base diffs for the redaction, store, file sink, extensions, records, cursor_follow, tail command and CHANGELOG. I only spot-checked the doc and skill pages. I did not read telescope_events_command.dart, telescope_files_command.dart or the test bodies line by line.

@anilcancakir

Copy link
Copy Markdown
Member Author

Thanks. Round 1, addressed in f27f435:

  • Major, lib/src/telescope_file_sink.dart:110 (data loss on a same-second restart): _rotate now opens with FileMode.append and seeds the tracked byte count from the file's existing length, so a sink started again in the same second keeps the earlier lines and rotates at the right size. Name scheme unchanged. Red-first tests cover start while another sink runs, stop then start, and rotation counting the bytes already on disk.
  • Minor, lib/src/extensions/register_telescope_extensions.dart:352 (tied atUs at a page boundary): a since page cut by limit now extends over every following record that shares the last atUs, so the cursor skips nothing. The docblocks say a page may exceed limit by those ties. Red-first tests for events and console.
  • codecov/patch: the added lines are now 458/461 covered (99.35%), with new tests for followCursor and the --follow loops on the real clock, and for a flush after a failed rotation. The three remaining lines are the catch around a failing RandomAccessFile.close() in _halt.

@kodizm please re-review.

@kodizm

kodizm Bot commented Oct 10, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

Both findings from the last round are fixed in f27f435, and I have nothing new to flag. This looks ready to merge.

What changed since the last review: the fix commit touches only _rotate in the file sink and _page in the read extensions, plus tests and docs. I re-read both functions and their callers.

  • lib/src/telescope_file_sink.dart:371: _rotate now opens with FileMode.append and seeds _TimelineFile.bytes from opened.length().
    • A same-second restart now keeps the lines the earlier sink flushed.
    • _write checks the rotation threshold against the bytes already in the file.
    • start() still awaits _current?.stop() before the new sink opens anything, so two sinks never append to one file at the same time.
  • lib/src/extensions/register_telescope_extensions.dart:360: on a since read, _page now continues past limit until the records sharing the last atUs run out.
    • The cursor (the largest atUs returned) combined with the strict > filter can no longer strand a tied record.
    • The newest-N path without since is unchanged.
    • Both docblocks now say a page can run over limit by those ties.

Tests

  • File sink: new tests cover starting while another sink runs, stop then start, and rotation that counts bytes already on disk.
  • Tied atUs paging: new tests for both events and console.
  • Follow paths: new tests for followCursor and the --follow loops.

CI

  • Lint & Test: success
  • codecov/patch: success (99.36% of the diff is covered, against a 97.88% target)
  • Auto-merge low-risk Dependabot PRs: skipped

Not re-read this round: the doc and skill pages, telescope_events_command.dart and telescope_files_command.dart, and the test bodies line by line. Those parts of this round's review rest on the commit stat and the CI results.

@anilcancakir
anilcancakir merged commit 5fb841c into master Oct 10, 2026
4 checks passed
@anilcancakir
anilcancakir deleted the feature/host-timeline branch October 10, 2026 09:29
@anilcancakir anilcancakir mentioned this pull request Oct 10, 2026
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