Conversation
|
/ai-review |
There was a problem hiding this comment.
Superseded by a newer AI review
🤖 AI Review
Reconciled all 7 Claude and 8 Codex findings into 13 deduplicated entries. Confirmed the code-level issues, including one major shutdown-bound failure. The concurrency and legacy-output findings are narrowed to account for owner lifecycle serialization and the CLI's existing line framing. Verification used surrounding source code, trusted conventions, and isolated UTF-8 and history-selection reproductions.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟠 MAJOR | packages/stack/src/host/LogStore.ts:758 |
shutdown |
codex | The five-second drain timeout does not bound shutdown because persistence batches, including filesystem writes and retention, are uninterruptible. |
| 🟡 MINOR | packages/stack/src/host/LogStore.ts:752 |
correctness |
claude+codex | Late partial lines from ended launches do not flush after two seconds of quiet unless another output batch arrives or the store closes. |
| 🟡 MINOR | packages/stack/src/host/LogStore.ts:860 |
concurrency |
claude | Concurrent calls to the store's remove and close operations can detach the same handle twice, leaving the second caller blocked forever. |
| 🟡 MINOR | packages/stack/src/Owner.ts:653 |
behavior-change |
claude | The legacy logs RPC now buffers unterminated output and normalizes carriage-return progress updates into newline-terminated records, changing raw RPC and package output. |
| 🟡 MINOR | packages/stack/src/host/LogStore.ts:725 |
error-handling |
claude+codex | Failed partial-line flushes can disappear without a lost marker, and previously committed lost markers can be duplicated when a later write in the same append fails. |
| 🟡 MINOR | packages/stack/src/host/LogStore.ts:893 |
performance |
claude | Sequential instance detachment allows shutdown drain time to accumulate across instances instead of sharing one shutdown budget. |
| 🟡 MINOR | packages/stack/src/host/LogStore.ts:425 |
correctness |
codex | The backward history scan can stop before records needed for the timestamp-ordered tail or since result. |
| 🟡 MINOR | packages/stack/src/host/LogStore.ts:351 |
correctness |
codex | Readers silently skip deleted interior generations when an older undeletable segment remains. |
| 🟡 MINOR | packages/stack/src/host/LogRecord.ts:284 |
correctness |
codex | A UTF-8 character split across three chunks loses its first-byte timestamp. |
| 🟡 MINOR | packages/stack/src/host/LogStore.ts:556 |
error-handling |
codex | Windows sharing-violation retries do not recognize segment-listing failures after they have been wrapped in LogStoreError. |
| 🟡 MINOR | packages/stack/src/host/LogStore.integration.test.ts:72 |
test-cleanup |
codex | Integration tests that ignore openStore's returned close operation leave the independently created store scope and its resource finalizers unclosed. |
| ⚪ NIT | packages/stack/src/host/LogStore.ts:522 |
documentation |
claude | The live-tail documentation incorrectly describes its PubSub as unbounded and the recipe buffer as the only place output can be dropped. |
| ⚪ NIT | packages/stack/src/effect.ts:1039 |
input-validation |
claude | The public readStackLogs API accepts invalid tail values without validating that they are non-negative integers. |
Stats
Claude findings: 7 · Codex findings: 8 · Confirmed: 13 · Refuted: 0 · Uncertain: 0
Models: claude-opus-5-5 + gpt-6.1-sol · Trigger: manual · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
Supabase CLI previewnpx --yes https://pkg.pr.new/supabase/cli/supabase@3990b4f9d3ce97d4fec88ea3f8962c11c107b333Preview package for commit |
a47d306 to
19bd0eb
Compare
Service output lived only in a per-recipe in-memory buffer, so the stack kept no log history, lost it when the owner exited, and native mode had nothing to replay. The owner now writes every instance's output to `logs/<service>/<instanceId>/<generation>.log` under the stack's state directory. Output chunks are tagged with their launch, process part, sequence and publish time at the source, so lines are split once, never glued across launches, and upstream loss is recorded as `lost`. Segments rotate at 5 MiB with byte and count retention, and are removed on instance and stack destroy. A single position-based reader serves history, follow and offline reads. It is exposed as a `readLogs` RPC and `readStackLogs`; the existing `logs` RPC keeps its live-only behaviour for `supabase stack logs`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Studio's Logs pages stayed empty because nothing forwarded the stack's service output to Analytics. The owner now ships persisted Auth, REST, Realtime, Storage, Functions and database lines to their Logflare sources, using the legacy Vector remaps ported to TypeScript. It posts to Analytics' own backend only while the composed Analytics instance is running and healthy, so shipping never wakes it or keeps it awake. Each instance keeps a cursor beside its log files: lines written while Analytics sleeps are shipped after it wakes with their original timestamps. Events carry deterministic ids and Logflare de-duplicates on them, so failed posts are retried without duplicates. Auth and target errors pause shipping and keep the cursor; malformed bodies are skipped. The CLI no longer composes Vector. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`supabase stack logs` now prints retained history and exits by default. `-f/--follow` hands over from history to live output per instance without gaps, `--tail` (default 200), `--since <duration|ISO|start>` and a repeatable `--service` select what is shown, and history is read from the log files when the stack is down. Output keeps the `log-entry` contract with `source: "history" | "live"` and adds `log-marker` events. The legacy live-only `logs` RPC is replaced by `readLogs`, which the Promise client exposes as an async iterable. The experimental stack no longer has a Vector service: saved stacks that contain one are migrated on owner start, and leftover Vector files and containers are removed. `analytics.vector_port` stays in the config schema because the legacy `supabase start` still reads it. PostgREST now logs every request, and request lines reach Analytics with their method, path, protocol and status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The artifacts catalog is also the version table the legacy `supabase start` reads in slim mode, so dropping Vector's entry would silently fall back to the upstream Vector image there. Vector stays in the catalog as an artifact kind that only the legacy start runs; the experimental stack's service kinds, `supabase services` and stack prepare still exclude it. Docker-backed log tests get the same timeout guard as the package's other Docker tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
19bd0eb to
2ba2d89
Compare
- Abort log persistence 5 seconds into close, detaching instances under one deadline; aborted segments are retired and still release their handles. - Flush quiet late partial lines when their grace expires. - Count unwritten records as lost once, and clear lost markers only after they are written. - Read every segment for tails and report interior segment gaps with resumeAt. - Keep the first byte's time for UTF-8 characters split across chunks. - Retry wrapped sharing violations, claim removed instances atomically, and validate the readStackLogs tail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Flush a late partial line only after every chunk published before its grace deadline is processed, comparing publish times. - Report a lost marker when a reader finishes a segment and the next retained generation is not contiguous. - Treat stack logs --since as a value-consuming flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hold the newline chunk behind a blocked write while the grace flusher wakes past its deadline, so the test fails if the flush skips queued output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
/ai-review |
There was a problem hiding this comment.
🤖 AI Review
Both independent reviews were available. Verified all eight distinct findings: seven confirmed and one refuted. The major finding is a reproduced latest-launch filtering bug. Other confirmed findings concern migration contention, cleanup, follow diagnostics, redundant sweeps, and documentation. The reported global-flag scanning regression does not occur on the stack logs execution path.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟠 MAJOR | apps/cli/src/commands/experimental/stack/logs/logs.format.ts:116 |
correctness |
codex | --since start can discard the current launch's logs and retain an older launch when that older launch's first output arrives late. |
| 🟡 MINOR | packages/stack/src/State.ts:686 |
robustness |
claude | Migration acquires the global registry lock even when no Vector instances exist, introducing an unnecessary contention failure into owner startup and orphan reclamation. |
| 🟡 MINOR | packages/stack/src/State.ts:672 |
resource-cleanup |
codex | Vector migration leaves the stack-owned vector.rendered.yaml behind for instances that used a custom pipeline. |
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/logs/logs.handler.ts:179 |
error-handling |
claude | --follow silently skips selected instances missing from the subsequently listed handles; if all are missing, the command completes successfully without explaining why following ended. |
| ⚪ NIT | packages/stack/src/host/LogForwarder.ts:375 |
error-handling |
claude | Destroying a shipped instance while Analytics is serving can produce a misleading 'Log shipping ... paused' warning during normal cleanup. |
| ⚪ NIT | packages/stack/src/Sweep.ts:50 |
code-quality |
claude | Session-stack orphan reclamation performs two container sweeps: one explicitly before migration and another through the namespace destroy path. |
| ⚪ NIT | apps/cli/src/command-internal/colors.ts:81 |
documentation |
codex | The new magenta and blue helpers describe bright colors but select normal ANSI colors. |
Refuted findings (kept for transparency, not posted as review comments)
apps/cli/src/command-internal/db-target-flags.ts:152(correctness): Adding boolean -f to stack logs causes the global explicit-false scanners to skip a following --experimental=false or --yes=false token.
Refuted: The cited helper has the described arity assumption, but it is not invoked on the stack logs execution path. The explicit-false scans run through resolveYes, resolveExperimental, and resolveDebugWithProjectEnv, which this command does not use. Stack registration instead uses resolveExperimentalFeature (stack-backend.ts:92-96), and telemetry correctly overrides f to follow (logs.command.ts:72; command-telemetry.ts:192-200). The claimed command-level regression therefore does not follow from this PR.
Stats
Claude findings: 5 · Codex findings: 3 · Confirmed: 7 · Refuted: 1 · Uncertain: 0
Models: claude-opus-5-5 + gpt-6.1-sol · Trigger: manual · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
| yield* Owner.sweepContainers(saved, dataRoot); | ||
| yield* state.migrate(id); | ||
| if (saved.lifetime === "session") | ||
| yield* destroyStack(state, saved, dataRoot, options.cacheRoot); |
There was a problem hiding this comment.
⚪ NIT · code-quality · source: claude
Session-stack orphan reclamation performs two container sweeps: one explicitly before migration and another through the namespace destroy path.
Evidence: Sweep.ts:50-53 invokes sweepContainers and then destroyStack for session stacks. Sweep.ts:15-20 invokes Owner.namespace.destroy, whose Owner.ts:741-743 pipeline invokes sweepContainers again.
Suggested fix: Allow reclamation to reuse proof of its completed preliminary sweep when invoking destruction. Preserve the preliminary sweep before deleting files that leftover Vector containers may still mount.
- Keep launch ids increasing per instance across owner restarts so stack logs --since start always selects the latest launch. - Migrate saved Vector stacks without taking the registry lock when there is nothing to migrate, and remove the rendered Vector config. - Fail stack logs --follow when the running stack serves none of the selected instances, and warn about skipped ones. - Detach an instance from log shipping, after its cursor write lands, before its logs are removed. - Remove readStackLogs, the end read option and unused platform options; reuse existing helpers and trim redundant tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… one An instance saved before launch ids were persisted resumes after the highest launch id at the end of its newest log segment, so its next launch never reuses an id its logs already hold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
In the experimental stack, service output lived only in an in-memory buffer:
supabase stack logshad no history, nothing survived an owner exit, and Studio's Logs pages were always empty because nothing reached Analytics. This PR persists every service's output, ships it to Analytics, and givesstack logsa history-first interface.Persisted logs
lostrecord.<stateRoot>/<stackId>/logs/<service>/<instanceId>/<generation>.log, one record per line (<time> <kind> <launchId> | <text>). Segments are immutable once closed, rotate at 5 MiB, and are retained by bytes (10 MiB) and count (64). Lines over 32 KiB are cut and flaggedtruncated.Studio Logs: shipping to Analytics
cursor.jsonnext to the log files lets lines written while Analytics slept ship after it wakes, with their original timestamps. Retained history is shipped the first time an instance is seen.PGRST_LOG_LEVEL=info), and request lines reach Analytics with method, path, protocol and status.supabase stack logs-f/--followhands over to live output per instance from the last printed record, without gaps or duplicates.--tail N(default 200),--since <duration|ISO|start>, repeatable--service. History is streamed with bounded memory and read from the files when the stack is down;-ffails before any output when the stack is not running.stream-jsonkeeps thelog-entryenvelope withsource: "history" | "live"and record timestamps, and addslog-markerevents.--output-format jsonprints an array.logsRPC is replaced byreadLogs({ id, since?, tail?, follow?, from? }); the Promise client exposes it as an async iterable.streamStackLogsreads a stopped stack. Launch ids keep increasing per instance across owner restarts, so--since startalways means the latest launch.Vector removed from the experimental stack
supabase startstill runs Vector, so its artifact pin stays in the catalog (which legacy slim mode reads) under a separate artifact kind; stack service kinds,supabase servicesand stack prepare exclude it.analytics.vector_portstays in the config schema for the same reason; the experimental stack ignores it.Terminal captures
Recorded from this branch's source on macOS: terminal with vhs, Studio in headless Chrome, both running at the same time against the same stack. A copy of the
usebasejump/basejumpsample with Analytics enabled and a smallhellofunction generates the traffic. Before this change, Studio's Logs pages were empty andstack logsprinted live output only.supabase stack start(Docker, lazy): no Vector member.Docker:
stack logs -f(top) follows REST, Auth, Storage and Functions while requests run (bottom); lazy services wake on the first request, and Studio's PostgREST, Auth and Storage pages show the same lines.Docker, Analytics asleep: requests are written to the log files without waking Analytics; opening Studio wakes it, and the earlier lines arrive with their original timestamps.
Native runtime: the same flow.
stack logshistory:--tail,--since 10m,--since startandstream-json.Stopped stack (native): history is read from the files, and
-ffails before printing.Supersedes #6864.
🤖 Generated with Claude Code