feat: git warp seek — time-travel through graph history#21
feat: git warp seek — time-travel through graph history#21flyingrobots merged 13 commits intomainfrom
Conversation
Introduces cursor-based time travel for exploring graph history: - `seek --tick N` positions cursor at a specific Lamport tick - `seek --save NAME` / `--load NAME` for named bookmarks - `seek --latest` returns to the present - `discoverTicks()` walks history without expensive reads - `materialize()` accepts `ceiling` param for partial replay - `info`, `materialize`, and `history` commands respect active cursor - ASCII seek renderer with timeline, writer activity, and graph stats
Updates CHANGELOG, README, GUIDE, and package.json for the new `git warp seek` time-travel command added in the previous commit.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a cursor-based time-travel "seek" feature: CLI commands and wiring, persistent cursor refs and parsing, ceiling-aware materialization and tick discovery in WarpGraph, ASCII seek renderer and op-summary extraction, docs/demo/changelog updates, and extensive unit and CLI tests. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as bin/warp-graph.js
participant WarpGraph as src/domain/WarpGraph.js
participant Persistence
participant Renderer as ascii/seek.js
User->>CLI: git warp seek --tick 5
CLI->>CLI: parseSeekArgs(), resolveTickValue()
CLI->>Persistence: readActiveCursor (refs/warp/.../cursor/active)
Persistence-->>CLI: cursor blob
CLI->>WarpGraph: discoverTicks()
WarpGraph->>Persistence: read writer patch chains
Persistence-->>WarpGraph: patches with lamport ticks
WarpGraph-->>CLI: ticks, perWriter, maxTick
CLI->>WarpGraph: materialize({ ceiling:5 })
WarpGraph->>WarpGraph: _materializeWithCeiling(5)
WarpGraph->>Persistence: replay patches up to ceiling
Persistence-->>WarpGraph: materialized state
WarpGraph-->>CLI: materialized payload + cursor metadata
CLI->>Persistence: writeActiveCursor({ tick:5 }) / save/load as needed
Persistence-->>CLI: cursor persisted / listed / dropped
CLI->>Renderer: renderSeekView(payload)
Renderer-->>CLI: ASCII dashboard
CLI-->>User: rendered seek dashboard
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Release Preflight
If you tag this commit as |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/GUIDE.md (1)
1009-1016:⚠️ Potential issue | 🟡 MinorThe
--view seekcommand is missing from the "Supported commands" table.It's shown in the example block at line 907 (
git warp --view seek) but not listed in the table here. Adding it keeps the reference consistent.Suggested addition
| `--view materialize` | Progress dashboard with statistics | +| `--view seek` | Time-travel dashboard with timeline |
🤖 Fix all issues with AI agents
In `@bin/warp-graph.js`:
- Around line 1606-1667: readActiveCursor, readSavedCursor and listSavedCursors
assume blobs are valid JSON with a numeric tick and will crash or produce
invalid _seekCeiling on corrupted data; add a shared parser/validator (e.g.,
parseCursorBlob) that takes the blob, tries JSON.parse inside a try/catch,
verifies the resulting object has a numeric tick (and any other required
fields), and either returns the validated object or throws a friendly E_USAGE
error (or returns null) as appropriate; call this parser from readActiveCursor
and readSavedCursor after readBlob, and from listSavedCursors before pushing
each cursor so corrupted entries are handled gracefully.
In `@CHANGELOG.md`:
- Line 33: Update the CHANGELOG entry so the helper names match the exported
API: replace the listed buildCursorRef(), buildSavedCursorRef(),
buildSavedCursorPrefix() with the actual exported names buildCursorActiveRef(),
buildCursorSavedRef(), and buildCursorSavedPrefix() in the RefLayout line so
searching the changelog matches the implementation.
In `@src/domain/WarpGraph.js`:
- Around line 741-748: The fast-path in _materializeWithCeiling incorrectly
returns receipts: [] on a cache hit, so when collectReceipts is true bypass the
cached-state fast path: in _materializeWithCeiling check collectReceipts before
returning this._cachedState/receipts and fall through to the full
materialization logic (or alternatively store/return cached receipts alongside
this._cachedState and this._cachedCeiling); update the branch that tests
this._cachedState && !this._stateDirty && ceiling === this._cachedCeiling to
avoid returning an empty receipts array when collectReceipts is truthy.
- Around line 721-726: The _resolveCeiling method currently ignores an explicit
null because it checks for options.ceiling !== undefined && options.ceiling !==
null; change the check to detect whether the caller provided a ceiling property
(even if null) — e.g. use Object.prototype.hasOwnProperty.call(options,
'ceiling') or ("ceiling" in options) — and return options.ceiling when the
property is present; otherwise fall back to this._seekCeiling. This ensures
callers can override/clear _seekCeiling by passing ceiling: null.
In `@src/visualization/renderers/ascii/seek.js`:
- Around line 25-26: The current construction of allPoints in
src/visualization/renderers/ascii/seek.js unconditionally prepends 0 which can
create a duplicate if ticks already contains 0; change the logic that creates
allPoints (the allPoints variable derived from ticks) to only prepend 0 when
ticks does not already include 0 (e.g., check ticks && ticks.includes(0) or
ticks[0] === 0) so that allPoints becomes either [0, ...ticks] or [...ticks]
accordingly; update any downstream assumptions about allPoints length if needed.
- Around line 20-69: The labels drift because labels only reserve segLen spaces
for the segment but not the dot character itself, so multi-char tick labels
shift following labels; in buildSeekTimeline (variables displayPoints, segLen,
timeline, labels) ensure labels reserve space for the dot before appending the
tick string (e.g., add one extra space per dot by using ' '.repeat(segLen + 1)
when i > 0 and likewise ensure the first tick label accounts for the initial
dot) so that the labels string and timeline string grow in lockstep and
multi-digit ticks stay vertically aligned under their dots.
🧹 Nitpick comments (2)
src/domain/utils/RefLayout.js (1)
1-13: Module docstring doesn't document the newcursor/*ref paths.The header lists the ref layout but omits the newly added
cursor/activeandcursor/saved/<name>paths. Keeping it in sync helps future contributors discover the full ref namespace at a glance.Suggested addition
* Ref layout: * - refs/warp/<graph>/writers/<writer_id> * - refs/warp/<graph>/checkpoints/head * - refs/warp/<graph>/coverage/head + * - refs/warp/<graph>/cursor/active + * - refs/warp/<graph>/cursor/saved/<name>test/bats/cli-seek.bats (1)
127-139:--tick -1may be fragile with some CLI argument parsers.Negative numbers as option values are a common source of parser ambiguity (e.g., yargs/commander may interpret
-1as a flag). Since these tests are passing, the parser handles it today — but consider using--tick=-1syntax for robustness against future parser changes or library upgrades.
…G names - Add parseCursorBlob() utility with JSON + numeric tick validation; replace raw JSON.parse in readActiveCursor, readSavedCursor, and listSavedCursors with descriptive errors on corrupted data. - Add --view seek to GUIDE.md supported commands table. - Fix CHANGELOG RefLayout helper names to match exported API. - 11 new unit tests for parseCursorBlob.
…ignment - _materializeWithCeiling: bypass cache when collectReceipts is true so callers get real receipts instead of empty array. - _resolveCeiling: use 'ceiling' in options so explicit null clears _seekCeiling and materializes latest state. - buildSeekTimeline: skip prepending 0 when ticks already contains it; compute target column positions for labels so multi-digit ticks stay aligned under their dots. - RefLayout.js: add cursor/* paths to module docstring. - 4 new regression tests (2 domain, 2 renderer).
Avoids parser ambiguity where -1 or +1 could be misinterpreted as flags by future CLI parser upgrades.
Release Preflight
If you tag this commit as |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@bin/warp-graph.js`:
- Around line 1950-1964: The no-cursor status branch currently returns hardcoded
nodes:0, edges:0, patchCount:0; instead, materialize the graph at maxTick like
the active-cursor path to compute real counts. In the branch that builds the
payload (where cursor: { active: false }) call the same
materialization/inspection logic used in the active-cursor status path to obtain
node/edge counts and patchCount, then populate nodes, edges, and patchCount
accordingly before returning the payload (keep perWriter:
serializePerWriter(perWriter), tick/maxTick/ticks and exitCode as-is). Ensure
you reuse the existing materialization routine rather than duplicating heavy
logic.
- Around line 1311-1315: The code mutates the internal field graph._seekCeiling
(seen in materializeOneGraph and handleSeek) instead of using the public API;
change these sites to call the public materialize method with the ceiling option
(e.g., after opening the graph with WarpGraph.open(...) in materializeOneGraph
and inside handleSeek, replace assignments to graph._seekCeiling with await
graph.materialize({ ceiling }) so the ceiling is passed as an option rather than
setting the private field).
In `@docs/GUIDE.md`:
- Around line 916-937: Docs show examples of signed tick arguments using `--tick
+1`/`--tick -1` which can be ambiguous to the CLI parser; update the examples
for the `git warp seek` command (and any related examples in GUIDE.md) to use
the `--tick=` form for signed values (e.g., `--tick=+1` and `--tick=-1`) and
mention that signed values should use the `=` form to match the BATS tests and
avoid parser ambiguity.
In `@src/visualization/renderers/ascii/seek.js`:
- Around line 26-37: When sampling points into displayPoints (using allPoints,
MAX_TIMELINE_WIDTH and step), ensure currentTick is always present: after
building displayPoints (first, last, and interior sampled indices), check if any
point.index or tick equals currentTick; if not, find the closest sampled index
in displayPoints to currentTick and replace that element with the actual
allPoints entry for currentTick (or nearest exact match in allPoints) so the
active dot/pointer logic referencing currentTick will always render; adjust
selection logic around displayPoints, step and Math.round to use the
replacement.
🧹 Nitpick comments (7)
test/unit/visualization/ascii-seek-renderer.test.js (2)
153-176: Guard clause may let the test pass vacuously if timeline rendering changes.The
if (timelineLine)guard on line 172 means that if the timeline line is not found (e.g., due to a rendering change), the test silently passes without asserting anything. Consider failing explicitly if no timeline line is found.Suggested fix
const timelineLine = lines.find((l) => /[●○\u25CB\u25CF]/.test(l)); - if (timelineLine) { - const dotCount = (timelineLine.match(/[●○\u25CB\u25CF]/g) || []).length; - expect(dotCount).toBe(3); - } + expect(timelineLine).toBeDefined(); + const dotCount = (timelineLine.match(/[●○\u25CB\u25CF]/g) || []).length; + expect(dotCount).toBe(3);
196-216: Same vacuous-pass pattern here — consider assertingtimelineIdx >= 0.- if (timelineIdx >= 0) { + expect(timelineIdx).toBeGreaterThanOrEqual(0); + {This ensures the test fails loudly if the timeline output format changes unexpectedly.
bin/warp-graph.js (1)
1262-1275: History handler builds cursor info manually instead of usingapplyCursorCeiling.Other commands (
handleQuery,handlePath,handleCheck) useapplyCursorCeilingto read and apply the cursor.handleHistoryreads the cursor directly and filters patches in its own code path. While this works (history doesn't need materialization), the inconsistency makes it easy to miss ifapplyCursorCeilinglogic changes later.test/bats/cli-seek.bats (4)
14-24: Consider asserting the shape ofticksentries, not just array length.The test checks
len(data["ticks"]) > 0but doesn't validate the structure of individual tick entries. If the schema oftickschanges (e.g., from integers to objects), this test would still pass silently.
141-153: Cross-command cursor test is valuable — consider adding a negative case too.This test validates that
queryrespects the active cursor, which is a key integration point. A complementary test verifying that after--latest, the query returns the full (unconstrained) node set would strengthen confidence in cursor clearing.
155-159: Plain-text test is minimal —grep -q "demo"is a weak assertion.This only confirms the word "demo" appears somewhere in the output. It wouldn't catch regressions where the seek view format is broken but the graph name still appears (e.g., in an error message). Consider checking for a more specific seek-related string (e.g., "tick" or "cursor").
Proposed improvement
`@test` "seek plain text output" { run git warp --repo "${TEST_REPO}" --graph demo seek assert_success - echo "$output" | grep -q "demo" + echo "$output" | grep -q "demo" + echo "$output" | grep -qiE "tick|cursor" }
18-23: JSON assertions viapython3env var pattern works but is fragile at scale.Passing
$outputthrough an environment variable has OS-level size limits (typically ~128KB–2MB depending on platform). This is fine for these small test payloads, but worth noting if future tests produce larger JSON output. An alternative would be piping through stdin:echo "$output" | python3 -c "import json,sys; data=json.load(sys.stdin); ..."Not blocking — the current approach is fine for this test suite's scope.
Also applies to: 30-36, 46-50, 60-64, 80-85, 98-103, 119-124, 134-138, 148-152
- Fix ASCII graph arrowheads being swallowed by node bounding boxes - Add seek demo GIF (docs/seek-demo.gif) showing time-travel with graph topology changes while worktree stays clean - Embed seek demo in README CLI section - Extract op summary renderer into shared module (opSummary.js) - Rework seek ASCII renderer with improved swimlanes and receipts - Add ROADMAP backlog section (structural diff, git-cas cache) - Bump version 10.3.0 → 10.3.1
git warp seek
Release Preflight
If you tag this commit as |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@bin/warp-graph.js`:
- Around line 1858-1884: The allPoints array in resolveTickValue blindly
prepends 0 which can duplicate an existing 0 from ticks; change construction of
allPoints so 0 is added only if not already present (e.g., check
ticks.includes(0) or deduplicate while preserving order), keeping the rest of
the logic using currentTick, ticks, startIdx and targetIdx unchanged so
indexOf(currentTick) and relative stepping behave correctly.
- Around line 1765-1841: parseSeekArgs currently accepts --tick=VALUE but
rejects --save=NAME, --load=NAME and --drop=NAME; update parseSeekArgs to also
recognize arg.startsWith('--save='), arg.startsWith('--load='), and
arg.startsWith('--drop=') (similar to the existing --tick= branch): when such an
arg is seen, ensure spec.action is 'status', set spec.action to
'save'/'load'/'drop' and set spec.name to the substring after the "="; keep
existing validation (use usageError when action already set or name missing) and
leave the existing space-separated handling intact so both forms work; reference
parseSeekArgs, spec, and usageError when making the changes.
In `@src/domain/WarpGraph.js`:
- Around line 763-768: The cache check in _materializeWithCeiling uses
_cachedState/_stateDirty/_cachedCeiling but not the current writer frontier, so
it can return stale state when refs/tips advance; modify the caching logic to
store a snapshot of the frontier (e.g., set this._cachedFrontier when populating
this._cachedState) and on each call to _materializeWithCeiling compare the
current frontier snapshot to this._cachedFrontier and bypass/invalidates the
cache if they differ (i.e., return a fresh materialization when frontier
changed) — update cache population and the early-return condition accordingly to
reference _cachedFrontier along with _cachedCeiling/_stateDirty.
🧹 Nitpick comments (3)
docs/seek-demo.tape (1)
19-27: Make the demo tape portable by derivingPROJECT_ROOT.The hard-coded path ties recording to one machine. Capturing the repo root before switching to the temp repo lets others re-run the tape without edits.
♻️ Suggested update
- Type "TMPDIR=$(mktemp -d) && cd $TMPDIR && git init -q sandbox && cd sandbox" + Type "export PROJECT_ROOT=$(pwd)" + Enter + Type "TMPDIR=$(mktemp -d) && cd $TMPDIR && git init -q sandbox && cd sandbox" @@ - Type "export PROJECT_ROOT=$HOME/git/git-stunts/git-warp" - Enterbin/warp-graph.js (2)
2376-2381:emitCursorWarningalways receivesnullformaxTickfrom non-seek commands.Every call site passes
nullas the second argument (lines 986, 1079, 1126, 1269, 1377), so the warning banner never shows "of {maxTick}". If this is intentional to avoid the overhead ofdiscoverTicks()in non-seek commands, consider updating the JSDoc to clarify, or consider piggybacking ondiscoverTicks()when a cursor is active.Also applies to: 984-986, 1077-1079, 1125-1126, 1264-1269
1264-1275: History filtering by cursor tick is correct butmaxTickis never populated.Line 1267 sets
maxTick: nullincursorInfo, and it's never updated. This is only used byemitCursorWarning(which handles null gracefully), so there's no functional issue — just a dead field in this context.
…ntax
- Replace graph._seekCeiling mutation in materializeOneGraph and
handleSeek with graph.materialize({ ceiling }) public option.
- Ensure the seek renderer always includes the active cursor tick in
the timeline window, even when the tick is absent from discovered
ticks (inserts at correct sorted position before windowing).
- Update GUIDE.md, README.md, and CHANGELOG examples to use --tick=+N
/ --tick=-N equals form for signed relative values, matching BATS
tests and avoiding CLI parser ambiguity.
- Replace manual cursor reading in handleHistory with applyCursorCeiling for consistency with query/path/check handlers. - Assert tick entries are integers, not just that the array is non-empty. - Add negative test: query returns full node set after --latest clears the cursor. - Strengthen plain-text seek output assertion to check for tick/cursor keywords, not just the graph name.
Release Preflight
If you tag this commit as |
- _materializeWithCeiling now snapshots the writer frontier alongside the ceiling and invalidates the cache when tips advance, preventing stale state when new writers appear or patches are appended. - resolveTickValue avoids duplicating tick 0 when ticks already contains it. - Fix BATS seek tests: correct JSON key (nodes, not state.nodes), use tick 0 → latest for cursor-clearing test, switch cross-command tests to stdin piping, add receipt/diff/frontier-change assertions. - Pre-push hook now runs all BATS test files in test/bats/.
Work around empty $output from BATS run when query command emits a cursor warning to stderr — capture to a temp file instead.
Release Preflight
If you tag this commit as |
The file was untracked so Docker builds (which copy from the git context) never included it, causing test 55 to fail with MODULE_NOT_FOUND on Node 20.
Release Preflight
If you tag this commit as |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/domain/WarpGraph.js`:
- Around line 737-741: The _resolveCeiling method currently uses the 'in'
operator and returns options.ceiling even when it's undefined; change the
presence check to treat undefined as "not provided" so null remains valid.
Update _resolveCeiling to return options.ceiling only when options exists and
options.ceiling !== undefined (e.g., if (options && options.ceiling !==
undefined) return options.ceiling;), otherwise fall back to this._seekCeiling;
this preserves explicit null while preventing an undefined ceiling from being
used.
| _resolveCeiling(options) { | ||
| if (options && 'ceiling' in options) { | ||
| return options.ceiling; | ||
| } | ||
| return this._seekCeiling; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/domain/WarpGraph.js | sed -n '730,750p'Repository: git-stunts/git-warp
Length of output: 965
🏁 Script executed:
rg -n "_resolveCeiling" src/domain/WarpGraph.jsRepository: git-stunts/git-warp
Length of output: 150
🏁 Script executed:
rg -n "ceiling" src/domain/WarpGraph.js | head -30Repository: git-stunts/git-warp
Length of output: 2170
🏁 Script executed:
rg -n "materialize\s*\(" src/domain/WarpGraph.js | head -20Repository: git-stunts/git-warp
Length of output: 1329
🏁 Script executed:
rg -B3 -A3 "ceiling\s*:" src/ --type=jsRepository: git-stunts/git-warp
Length of output: 1165
🏁 Script executed:
sed -n '768,800p' src/domain/WarpGraph.jsRepository: git-stunts/git-warp
Length of output: 1288
🏁 Script executed:
fd -t f "test|spec" src/ --type=jsRepository: git-stunts/git-warp
Length of output: 232
🏁 Script executed:
rg -n "materialize\s*\(" --type=js test/ spec/ 2>/dev/null | head -20Repository: git-stunts/git-warp
Length of output: 1770
🏁 Script executed:
rg -B2 -A2 "\.materialize\(" src/ --type=js | head -40Repository: git-stunts/git-warp
Length of output: 2740
🏁 Script executed:
sed -n '595,620p' src/domain/WarpGraph.jsRepository: git-stunts/git-warp
Length of output: 1066
🏁 Script executed:
rg -n "ceiling\s*:" test/ --type=jsRepository: git-stunts/git-warp
Length of output: 1875
🏁 Script executed:
rg -n "undefined" src/domain/WarpGraph.js | grep -i ceilingRepository: git-stunts/git-warp
Length of output: 45
🏁 Script executed:
rg -B5 -A5 "ceiling\s*:\s*undefined" . --type=jsRepository: git-stunts/git-warp
Length of output: 45
🏁 Script executed:
grep -n "{ ceiling" test/unit/domain/ -RRepository: git-stunts/git-warp
Length of output: 1779
🏁 Script executed:
sed -n '380,395p' test/unit/domain/WarpGraph.seek.test.jsRepository: git-stunts/git-warp
Length of output: 585
🏁 Script executed:
rg -B10 -A10 "ceiling.*undefined" src/ test/Repository: git-stunts/git-warp
Length of output: 45
🏁 Script executed:
sed -n '768,800p' src/domain/WarpGraph.js | grep -A5 "ceiling <="Repository: git-stunts/git-warp
Length of output: 330
🏁 Script executed:
rg -B3 -A3 "materialize\s*\(\s*\{" test/ --max-count=20Repository: git-stunts/git-warp
Length of output: 17529
🏁 Script executed:
python3 << 'EOF'
# Check what happens when ceiling is undefined in _materializeWithCeiling
# Specifically the filter operation at the patch filtering stage
test_cases = [
("ceiling = 2", "5 <= 2 = False (would filter out patches with lamport > 2)"),
("ceiling = null", "5 <= null = False (would filter out all patches)"),
("ceiling = undefined", "5 <= undefined = False (would filter out all patches)"),
("ceiling = 0", "5 <= 0 = False (would filter out all patches)"),
]
for case, result in test_cases:
print(f"{case}: {result}")
EOFRepository: git-stunts/git-warp
Length of output: 333
🏁 Script executed:
rg -n "lamport <=" src/domain/WarpGraph.js -A2 -B2Repository: git-stunts/git-warp
Length of output: 1096
🏁 Script executed:
rg -n "lamport" src/domain/ --type-list | head -20Repository: git-stunts/git-warp
Length of output: 452
🏁 Script executed:
sed -n '800,850p' src/domain/WarpGraph.jsRepository: git-stunts/git-warp
Length of output: 1643
🏁 Script executed:
fd -t f "tsconfig\|package.json" . | head -5Repository: git-stunts/git-warp
Length of output: 45
🏁 Script executed:
head -50 package.jsonRepository: git-stunts/git-warp
Length of output: 1362
🏁 Script executed:
rg -n "ceiling.*undefined" src/ test/ 2>/dev/nullRepository: git-stunts/git-warp
Length of output: 45
🏁 Script executed:
rg -n "const.*ceiling\s*=" src/ -A2 -B2 | head -40Repository: git-stunts/git-warp
Length of output: 480
Guard against ceiling: undefined by treating it as "not provided".
The current implementation uses the 'in' operator to check if ceiling is present in options, then returns its value unconditionally. If a caller passes { ceiling: undefined }, the method returns undefined, which causes ceiling !== null at line 604 to evaluate true. This enters the ceiling-aware path with undefined as the ceiling value, making the filter at line 802 (entry.patch.lamport <= undefined) always false, resulting in an empty state.
While the JSDoc type signature declares {ceiling?: number|null} (excluding undefined), defensive handling to treat undefined as "not provided" aligns with the documented behavior where omitting the key falls through to _seekCeiling.
🛠️ Suggested fix
_resolveCeiling(options) {
- if (options && 'ceiling' in options) {
- return options.ceiling;
- }
+ if (options && 'ceiling' in options && options.ceiling !== undefined) {
+ return options.ceiling;
+ }
return this._seekCeiling;
}🤖 Prompt for AI Agents
In `@src/domain/WarpGraph.js` around lines 737 - 741, The _resolveCeiling method
currently uses the 'in' operator and returns options.ceiling even when it's
undefined; change the presence check to treat undefined as "not provided" so
null remains valid. Update _resolveCeiling to return options.ceiling only when
options exists and options.ceiling !== undefined (e.g., if (options &&
options.ceiling !== undefined) return options.ceiling;), otherwise fall back to
this._seekCeiling; this preserves explicit null while preventing an undefined
ceiling from being used.
- parseSeekArgs now accepts = form for --save, --load, --drop - seek-demo.tape derives PROJECT_ROOT from $(pwd) instead of hardcoding - Clarified emitCursorWarning/applyCursorCeiling JSDoc re: null maxTick
Release Preflight
If you tag this commit as |
The 'in' operator returned options.ceiling even when undefined; now uses !== undefined so explicit null still overrides the instance ceiling.
Release Preflight
If you tag this commit as |
Summary
git warp seek: Cursor-based time-travel that navigates to any Lamport tick, saves/loads named bookmarks, and materializes state at any point in history. Existing commands (query,info,materialize,history) respect the active cursor.--view seek): Swimlane timeline showing per-writer patches, included/excluded zones, op receipts, and graph stats at the selected tick.▼/▲/▶/◀arrowheads correctly when ELK endpoints land inside node bounding boxes.docs/seek-demo.gif): Animated walkthrough showing graph topology changing at each tick whilegit statusproves the worktree is untouched.Test plan
no-coordinationregression suite passesSummary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests
Chores