Skip to content

feat(dream): --archive-dir flag persists CycleReport JSON + atomic latest.json symlink#1133

Open
ChenyqThu wants to merge 1 commit into
garrytan:masterfrom
ChenyqThu:upstream-fix/dream-archive-dir
Open

feat(dream): --archive-dir flag persists CycleReport JSON + atomic latest.json symlink#1133
ChenyqThu wants to merge 1 commit into
garrytan:masterfrom
ChenyqThu:upstream-fix/dream-archive-dir

Conversation

@ChenyqThu
Copy link
Copy Markdown

Summary

Adds an --archive-dir <path> flag to gbrain dream that, when paired with --json, writes the CycleReport to <path>/<iso>.json (fs-safe colon-free timestamp) and atomically swaps a <path>/latest.json symlink to point at it.

Motivation

The cron pattern of "spawn dream --json, capture stdout, parse JSON, archive to a per-cycle file, swap a latest pointer" repeats across every downstream that wraps dream. Pushing the archive surface into core removes ~80 LoC from each wrapper while giving everyone the same atomic-swap contract (no dangling symlink ever visible to readers — rename is atomic on the same fs).

Concrete downstream case: my fork jarvis-knowledge-os-v2 ships a 205-line dream-wrap launchd worker whose primary value (after this PR lands) becomes the exit-code translation block (~30 lines). The entire archive + symlink surface (~80 lines) collapses into a single --archive-dir arg.

Behavior

  • --archive-dir without --json is a no-op + emits a stderr warning (printed human report has no JSON to archive)
  • Directory is auto-created (mkdirSync recursive)
  • ISO timestamp is colon-free (2026-05-17T23-45-12Z) so SMB / Windows shares accept the filenames
  • latest.json swap uses symlink-to-tmp + rename pattern (atomic on the same fs); on platforms where symlink isn't supported the per-cycle file still lands and a stderr warning fires
  • Cycle status semantics unchanged (--archive-dir affects archive side-effect only, not exit code or report shape)

Help text

  --archive-dir <path>
                      Persist the CycleReport JSON to <path>/<iso>.json and
                      atomically swap a <path>/latest.json symlink. Pairs with
                      --json; no-op without it. Replaces ad-hoc downstream
                      stdout-capture wrappers. Directory is auto-created.

Test plan

  • bun run typecheck — clean
  • bun test test/dream-cli-flags.test.ts test/dream.test.ts29 pass / 0 fail / 72 expect() calls / 19.46s
  • 5 new static-introspection cases in test/dream-cli-flags.test.ts (flag declared, archive helper exists, no-op-without-json contract, isoStamp colon-free contract)
  • 5 new integration cases in test/dream.test.ts against real PGLite + real archive dir:
    • per-cycle file + symlink shape
    • fs-safe filename (no :, sortable, matches regex)
    • atomic swap on second run (symlink target changes, both per-cycle files retained)
    • auto-creates missing destination directory
    • no-op without --json + stderr warning emitted

Diff size

3 files / +205 / -3 — additive flag, no behavior change for existing callers.

Related downstream cleanup

On merge, my fork drops skills/kos-jarvis/dream-wrap/run.ts from 205 LoC to ~30 (exit code translation only). Filed as PR-3 in my fork's TODO.

Pattern matches the garrytan/gbrain PR style of #1016 (additive AI recipe field) and the superseded #1017 (bootstrap probes) — fork-side experience codified into upstream so the wrapper layer thins.

🤖 Generated with Claude Code

…on symlink

Adds an --archive-dir <path> flag to `gbrain dream` that, when paired
with --json, writes the CycleReport to <path>/<iso>.json (fs-safe
colon-free timestamp) and atomically swaps a <path>/latest.json
symlink to point at it.

Motivation: the cron pattern of "spawn dream --json, capture stdout,
parse, archive, swap a latest pointer" repeats across every downstream
that wraps dream. Pushing the archive surface into core dream removes
~80 LoC from each wrapper while giving everyone the same atomic-swap
contract (no dangling symlink visible to readers — rename is atomic
on the same fs).

Concrete fork case: jarvis-knowledge-os-v2 has a 205-line dream-wrap
launchd worker whose primary value (after this PR lands) becomes the
exit-code translation block (~30 lines); the entire archive + symlink
surface (~80 lines) collapses into a single --archive-dir arg.

Behavior:
- --archive-dir without --json is a no-op + emits a stderr warning
  (printed human report has no JSON to archive)
- Directory is auto-created (mkdirSync recursive)
- ISO timestamp is colon-free (2026-05-17T23-45-12Z) so SMB / Windows
  shares accept the filenames
- latest.json swap uses symlink-to-tmp + rename pattern (atomic on
  same fs); on platforms where symlink isn't supported the per-cycle
  file still lands and a stderr warning fires
- Cycle status semantics unchanged (--archive-dir affects archive
  side-effect only, not exit code or report shape)

Tests (29 pass, 0 fail in 19.46s):
- test/dream-cli-flags.test.ts: 5 new static-introspection cases
  (flag declared, archive helper exists, no-op-without-json contract,
  isoStamp colon-free contract)
- test/dream.test.ts: 5 new integration cases against real PGLite
  + real archive dir
  (per-cycle file + symlink shape, fs-safe filename, atomic swap on
  second run, auto-creates missing dir, no-op without --json)

Typecheck: clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ChenyqThu pushed a commit to ChenyqThu/jarvis-knowledge-os-v2 that referenced this pull request May 17, 2026
…on 1)

Filed 2026-05-17 with 29 pass / 0 fail test coverage. On merge, fork
dream-wrap drops from 205 → ~30 LoC (exit-code translation only).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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