Skip to content

feat(journeys,core): add runtime.canGoBack / canGoForward predicates#41

Merged
kibertoad merged 3 commits into
mainfrom
feat/can-go-back
May 15, 2026
Merged

feat(journeys,core): add runtime.canGoBack / canGoForward predicates#41
kibertoad merged 3 commits into
mainfrom
feat/can-go-back

Conversation

@kibertoad
Copy link
Copy Markdown
Owner

@kibertoad kibertoad commented May 15, 2026

Summary

  • Adds JourneyRuntime.canGoBack(id) and canGoForward(id) — read-only predicates that mirror the guards in goBack / goForward, so shell-owned Back / Forward toolbars can gate enable-state authoritatively from a single instance id without duplicating the runtime's transition / entry opt-in logic.
  • Factors a single canGoBackFor(record, reg) helper as the sole authority on "would goBack(id) rewind?", used by bindStepCallbacks (step-prop closure), dispatchGoBack (call-site guard), and runtime.canGoBack(id). Same pattern for canGoForwardFor.
  • Behaviour fix in dispatchGoBack: now honours entry.allowBack: false on the active step's registered module descriptor. Previously runtime.goBack(id) could rewind out of a step the module had explicitly declared non-rewindable, contradicting the existing "Matches the closure form" JSDoc on JourneyRuntime.goBack. validateJourneyContracts already flagged this configuration as an error, so the change is observable only when validation is skipped — strictly defensive.

Test plan

  • pnpm vitest run in packages/journeys — 426 tests pass, including new regression tests for the entry opt-out scenario (canGoBack returns false AND runtime.goBack(id) no-ops when the active entry declares allowBack: false).
  • Type check passes (vitest typecheck integration).
  • oxlint clean on touched files (one pre-existing unused-import warning unrelated to this change).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added predicate methods to determine whether back/forward navigation is available, enabling better UI control over navigation button states.
  • Documentation

    • Enhanced navigation documentation with clearer guard conditions and behavior specifications.
    • Comprehensive test coverage added for navigation predicates.

Review Change Stack

Read-only predicates that mirror the guards in `goBack` / `goForward`
exactly, so a shell-owned Back / Forward toolbar can gate enable-state
authoritatively from a single instance id without duplicating the
runtime's transition / entry opt-in logic.

Factored a single `canGoBackFor(record, reg)` helper that's now the
sole authority on "would goBack(id) rewind?". Used by:
- `bindStepCallbacks` (gates the step-prop `goBack` closure)
- `dispatchGoBack` (rejects calls that should be no-ops)
- `runtime.canGoBack(id)` (new predicate)

This collapses three inline copies of the rule and fixes a latent
contradiction with the documented "Matches the closure form" promise:
previously `dispatchGoBack` skipped the `entryAllowBackMode` check,
so `runtime.goBack(id)` could rewind out of a step the module had
explicitly declared non-rewindable. `validateJourneyContracts` already
flagged this as a config error, but the runtime is now defensive when
validation is skipped.

Same pattern for `canGoForwardFor` / `dispatchGoForward` (no behavior
change there).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Warning

Rate limit exceeded

@kibertoad has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 50 minutes and 55 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 15021cd5-a2b8-492f-92dc-4d03eb2bbccb

📥 Commits

Reviewing files that changed from the base of the PR and between e7845bb and 50968cb.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • .github/workflows/zizmor.yml
  • packages/journeys/README.md
  • packages/journeys/src/runtime-go-forward.test.ts
📝 Walkthrough

Walkthrough

This PR adds two new public query methods, canGoBack(id) and canGoForward(id), to JourneyRuntime that return booleans indicating whether back or forward navigation would be permitted for a given journey instance. The implementation extracts shared guard logic, refactors existing dispatch and callback paths to use these predicates, and includes comprehensive test coverage across state transitions and edge cases.

Changes

Navigation Predicate API

Layer / File(s) Summary
Public API contract and documentation
packages/core/src/journey-contracts.ts, packages/journeys/README.md, CHANGELOG.md
Type signatures for canGoBack(id: InstanceId): boolean and canGoForward(id: InstanceId): boolean are added to the JourneyRuntime interface. Documentation clarifies that goBack is a no-op when the module entry declares allowBack: false and directs callers to use canGoBack to gate UI. Changelog and README document the new predicates as pure, guard-mirroring methods.
Runtime implementation with shared predicates
packages/journeys/src/runtime.ts
New canGoBackFor(record, reg) and canGoForwardFor(record) predicates centralize the guard logic for back/forward navigation enablement. dispatchGoBack, dispatchGoForward, and bindStepCallbacks are refactored to use these shared helpers instead of inline checks. Public canGoBack(id) and canGoForward(id) methods are added to the runtime object, delegating to the shared predicates and returning false for unknown or missing instances.
Test coverage for navigation predicates
packages/journeys/src/runtime-go-back.test.ts, packages/journeys/src/runtime-go-forward.test.ts
New test suites for canGoBack(id) validate unknown ids, initial/start state, transition allowBack requirement, state after advancing and rewinding, entry-level allowBack: false override that blocks navigation even when transition allows it, and disabled state for completed instances. New test cases for canGoForward(id) validate empty future stack, redo state transitions, unknown ids, and terminal instances.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • kibertoad/modular-react#40: Introduces the redo goForward/future stack feature that this PR builds upon with new state-query predicates.
  • kibertoad/modular-react#38: Introduces goBack(id) dispatch behavior that this PR extends by extracting and centralizing guard logic into reusable predicates.

Suggested labels

minor

Suggested reviewers

  • diogomiguel

Poem

🐰 A rabbit's rhyme for guards so keen,
Where back and forward paths are seen,
Predicates now ask "Can we go?"
Before the journey starts to flow,
Pure checks that shield from stumbling,
No mutations, no fumbling!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding two new predicate methods (canGoBack and canGoForward) to the JourneyRuntime API. It is specific, clear, and directly reflects the primary objective of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/can-go-back

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@kibertoad kibertoad requested a review from diogomiguel May 15, 2026 14:54
kibertoad and others added 2 commits May 15, 2026 17:58
Two issues with the CI workflow surfaced on this PR:

1. The reusable `ci.common.yml` declares `permissions: contents: read`
   on its build job, but the calling `build` job in `ci.yml` had no
   `permissions:` block. Combined with the workflow-level
   `permissions: {}` added in #39, the caller could not grant the
   permissions the reusable workflow needs, so every CI run since #39
   has been a `startup_failure` with zero jobs scheduled. Grant
   `contents: read` on the caller job so the reusable workflow can run.

2. `PATH_TO_NAME` in `changed-files-job` enumerated every workspace
   package except `packages/journeys`, so PRs touching only journeys
   produced an empty `packages` output and skipped the build matrix
   entirely. Add the missing entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kibertoad kibertoad merged commit 02be81b into main May 15, 2026
16 checks passed
@kibertoad kibertoad deleted the feat/can-go-back branch May 15, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant