fix: close index bootstrap recovery actions - #118
Merged
Conversation
z2z23n0
force-pushed
the
fix/close-bootstrap-next-action
branch
from
August 17, 2026 11:39
e02ab77 to
21778c8
Compare
Default find --cwd/--root/--selector now emits one closed sync command per source instead of falling back to Codex all-history alternatives.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem observed
We hit a concrete first-use failure while asking an agent to recover context from an earlier QA task:
index_unavailable.argv: ["shlog", "sync"].syncwrites local state, but the action did not say which resolved database/source/selector it would write and did not declare the side effect in a machine-readable form.From the user perspective, Sherlog was installed and the Skill was selected, but the first query still did not initialize Sherlog or produce a result.
Why this is a general contract bug
This is deterministic whenever the queried index is absent: a fresh installation, a new custom
--db, or an index that was moved or removed. Whether the user notices it depends on the host. A permissive host may reconstruct and run a broadsync; a conservative host will reject or avoid the ambiguous write. The underlying recovery contract is incomplete in both cases.The missing logic should not be added as Skill-side if/else branches. By the time the error is built, the CLI has already resolved the effective database, source, root, and selector. Asking the Skill or host to reconstruct those values duplicates CLI resolution rules and can silently sync the default Codex root or default database instead of the scope that failed.
This PR partially addresses #107, specifically P0.3 for
index_unavailablerecovery actions.Root cause
The read path preserved the resolved query context internally, but discarded it when converting a missing SQLite index into
AppError::IndexUnavailable. The error exposed a human-oriented suggestion, not a replayable host action:{ "argv": ["shlog", "sync"] }That shape has three problems:
--db, source, and selector resolved for the failed query;What changed
1. The CLI now owns recovery command construction
IndexBootstrapCommandbuilds the action only after the effective selector and database have been resolved. A single-source/scope failure now returns a command equivalent to:{ "selector": { "source": "codex", "kind": "cwd", "root": "...", "cwd": "..." }, "command": { "executable": "inherit", "args": [ "sync", "--source", "codex", "--selector", "<canonical-selector-key>", "--db", "<the-db-used-by-the-failed-query>", "--json" ], "sideEffect": "write_index" }, "argv": ["shlog", "sync", "...the same closed args..."] }argvremains as a compatibility mirror; the typedcommandis the authoritative action for hosts that understand it.2. Missing-index errors retain the failed command scope
findpreserves its resolvedall, cwd, root, or explicit selector;listpreserves its resolved selector;read-range,read-page, andstatspreserve the source-specific defaultallselector;find, where there is no single exact selector, keeps the bounded default Codex alternatives instead of pretending that one exact recovery scope exists.3. The Skill stays thin
The Skill adds one invariant rather than a recovery decision tree:
The Skill does not preflight with
status, rebuildsyncarguments, or unconditionally run a broad sync.Why this closes the loop
The resulting first-use flow is now explicit and policy-safe:
This preserves both important invariants:
The regression test exercises that exact loop: it runs a scoped
findagainst a missing custom database, executes the returnedcommand.argsunchanged, verifies that sync creates the requested index, then retries the original query and finds the expected session.Non-goals
This PR does not:
Validation
cargo fmt --all -- --checkcargo test --workspace --all-targets --all-features --lockedcargo clippy --workspace --all-targets --all-features --locked -- -D warningscargo build --release --locked --bin shlognpm run check