Skip to content

fix(brain-repo): allow restore wizard to list snapshots before connect#77

Open
mt-alarcon wants to merge 1 commit into
evolution-foundation:mainfrom
mt-alarcon:feat/brain-repo-wizard-snapshots-preview
Open

fix(brain-repo): allow restore wizard to list snapshots before connect#77
mt-alarcon wants to merge 1 commit into
evolution-foundation:mainfrom
mt-alarcon:feat/brain-repo-wizard-snapshots-preview

Conversation

@mt-alarcon
Copy link
Copy Markdown

@mt-alarcon mt-alarcon commented May 13, 2026

Problem

The restore wizard runs before `BrainRepoConfig` is persisted (it's a first-run / reconfigure flow). However, `/api/brain-repo/snapshots` and `/api/brain-repo/restore/start` both require a persisted config — they abort with `400 "Brain repo not connected"`.

This creates a catch-22: users selecting a repo via the wizard cannot list its snapshots, blocking the entire restore flow.

Solution

Both endpoints now accept an optional `token` + `repo_url` pair (query string on `/snapshots`, JSON body on `/restore/start`). When provided, they bypass the persisted-config requirement. When absent, the legacy fallback to `BrainRepoConfig` is preserved.

This mirrors the same opt-in token override that `/api/brain-repo/detect` already accepts.

Changes

Backend (`dashboard/backend/routes/brain_repo.py`):

  • `/snapshots`: accepts `?token=&repo_url=` query params
  • `/restore/start`: accepts `token` + `repo_url` in JSON body
  • Both fall back to stored config when absent (preserves compat for `/settings/brain-repo` and reconnect flows)

Frontend (`dashboard/frontend/src/pages/onboarding/restore/*.tsx`):

  • `RestoreFlow.tsx`: stores token in state, propagates through steps
  • `RestoreSelectRepo.tsx`: `onNext(url, token)` — passes token up
  • `RestoreSelectSnapshot.tsx`: accepts token prop, sends in query string
  • `RestoreExecute.tsx`: accepts token + repoUrl props, sends in body

Verification

  • `tsc --noEmit` exits 0
  • `py_compile` OK
  • Existing readers of `BrainRepoConfig` (settings/brain-repo, reconnect) unaffected — fallback path preserved

Impact

Unblocks the wizard restore flow for any user setting up a new EvoNexus instance from an existing brain repo (disaster recovery, new server migration, etc.).

Summary by Sourcery

Allow the restore wizard to list and execute brain repo snapshots using an explicit token and repo URL before a BrainRepoConfig is persisted.

New Features:

  • Enable /api/brain-repo/snapshots to accept token and repo_url overrides for listing snapshots without a stored brain repo configuration.
  • Allow /api/brain-repo/restore/start to accept token and repo_url in the request body to start restores without relying on persisted config.
  • Propagate token and repoUrl through the onboarding restore wizard steps so snapshot selection and restore execution can use the override-based backend flow.

The restore wizard ran before BrainRepoConfig was persisted, so
/api/brain-repo/snapshots and /api/brain-repo/restore/start aborted
with "Brain repo not connected". The only workaround was inserting
config directly via Python (see [C]contabo-brain-restore.py).

Both endpoints now accept an optional token + repo_url pair (query
string on snapshots, JSON body on restore/start). When provided they
bypass the persisted-config requirement; when absent the legacy
fallback to BrainRepoConfig is preserved, so /settings/brain-repo
and reconnect flows are unaffected.

Wizard now propagates the PAT from RestoreSelectRepo through
RestoreFlow into both RestoreSelectSnapshot and RestoreExecute.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 13, 2026

Reviewer's Guide

Backend brain-repo restore endpoints now accept an explicit token + repo_url so the onboarding restore wizard can list and restore snapshots before a BrainRepoConfig is persisted, while preserving the previous behavior when overrides are not provided. The frontend restore wizard threads token and repoUrl through all steps and attaches them to the snapshots and restore/start API calls.

Sequence diagram for restore wizard token and repo_url propagation

sequenceDiagram
    actor User
    participant RestoreFlow
    participant RestoreSelectRepo
    participant RestoreSelectSnapshot
    participant RestoreExecute
    participant Backend as BrainRepoBackend

    User->>RestoreFlow: start restore wizard
    RestoreFlow->>RestoreSelectRepo: render select-repo
    User->>RestoreSelectRepo: selectRepo(html_url) + token
    RestoreSelectRepo->>RestoreFlow: onNext(repoUrl, token)
    RestoreFlow->>RestoreSelectSnapshot: render select-snapshot(repoUrl, token)
    RestoreSelectSnapshot->>Backend: api.get(/brain-repo/snapshots?token&repo_url)
    Backend-->>RestoreSelectSnapshot: SnapshotData
    RestoreSelectSnapshot->>RestoreFlow: onNext(snapshot)
    RestoreFlow->>RestoreExecute: render execute(snapshot, token, repoUrl)
    RestoreExecute->>Backend: fetch(/api/brain-repo/restore/start, {ref, include_kb, token, repo_url})
    Backend-->>RestoreExecute: stream restore steps
    RestoreExecute-->>User: onComplete()
Loading

Flow diagram for snapshots and restore_start override vs config fallback

flowchart TD
    A[Request to /api/brain-repo/snapshots or /api/brain-repo/restore/start] --> B{token and repo_url provided?}
    B -- Yes --> C[Use override_token and override_repo_url]
    C --> D[get_repo_info or _resolve_repo]
    D --> E[list_snapshots or start_restore]
    B -- No --> F[_get_config]
    F --> G{config and github_token_encrypted?}
    G -- No --> H[abort 400 Brain repo not connected]
    G -- Yes --> I[_decrypt_token]
    I --> J{token decrypted?}
    J -- No --> K[abort 400 Could not decrypt stored token]
    J -- Yes --> L[Use config.repo_url and decrypted token]
    L --> E
Loading

File-Level Changes

Change Details Files
Allow /api/brain-repo/snapshots to operate either against an explicit token+repo_url override or the persisted BrainRepoConfig, matching the detect endpoint behavior.
  • Extended snapshots() docstring to document the override behavior and its purpose in the wizard flow.
  • Read optional token and repo_url query parameters, trimming whitespace.
  • When both override values are present, short-circuit to use get_repo_info and list_snapshots with the override token and derived owner/name, handling ImportError by returning empty snapshot structures.
  • Validate that repo_url resolves to a GitHub repo for the given token, aborting with 400 if owner/name are missing.
  • Wrap list_snapshots in a try/except, logging failures and returning a 400 with an error description.
  • Fall back to the existing _get_config() and encrypted-token flow when no valid overrides are supplied, preserving the old error path when the brain repo is not connected.
dashboard/backend/routes/brain_repo.py
Allow /api/brain-repo/restore/start to accept token+repo_url in the request body and bypass the persisted BrainRepoConfig requirement when provided, while keeping the legacy config-based path as a fallback.
  • Parse optional token and repo_url from the JSON body and trim whitespace.
  • Require ref as before, aborting with 400 when missing.
  • When override token and repo_url are provided, use them directly as the token and repo_url for the restore instead of loading BrainRepoConfig.
  • When overrides are absent, retain the previous behavior: require a stored config with an encrypted token, attempt to decrypt it, and abort with the existing 400 errors if config or token are unavailable.
  • Ensure repo_url is taken from the config only in the non-override path, prior to entering the generator that performs the restore.
dashboard/backend/routes/brain_repo.py
Thread token (and repoUrl where needed) through the onboarding restore wizard steps and attach them to the snapshots and restore/start API calls.
  • In RestoreFlow, add token state alongside repoUrl and snapshot, and update the step transitions so select-repo passes both repoUrl and token forward, and subsequent steps receive them as props.
  • Update RestoreSelectRepo to change the onNext signature to (repoUrl, token) and invoke it with the selected repo HTML URL plus the trimmed token from the detect step.
  • Update RestoreSelectSnapshot props to accept a token, and build the /brain-repo/snapshots URL with token and repo_url as query params when present, keeping the old URL when both are empty; include token in the effect dependency list.
  • Update RestoreExecute props to accept token and repoUrl, include both fields in the POST body to /api/brain-repo/restore/start alongside ref and include_kb, and add them to the effect dependency list so reruns pick up changes.
  • Keep existing behavior for error handling, progress tracking, and step transitions while only extending the data passed through the flow.
dashboard/frontend/src/pages/onboarding/restore/RestoreFlow.tsx
dashboard/frontend/src/pages/onboarding/restore/RestoreSelectRepo.tsx
dashboard/frontend/src/pages/onboarding/restore/RestoreSelectSnapshot.tsx
dashboard/frontend/src/pages/onboarding/restore/RestoreExecute.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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