Skip to content

lanes-index inventory follow-ups from Copilot rounds 3 and 4 on #171: unread-lane answers, partial listings, foreign-source marks, SQLite read errors, footer remedy #173

Description

@brettheap

Follow-up from Copilot's third review of #171 (PR for #161). The landing rule caps Copilot at two rounds, so a round-three finding goes into one issue here instead of into #171. This is that issue. It has one finding.

The finding: a first sync where one lane's inventory can't be read publishes an empty answer that looks valid

Copilot thread: #171 (comment) (commit f1e96c1, lanes-index around line 1916, run_inventory).

How it goes wrong. lanes-edit.sh worktrees --all reports a lane whose trees/ directory exists but can't be read as an UNREAD line. run_inventory then puts that lane in held. A held lane keeps its stored rows, and that part is correct: a failed read is not an answer. On the first sync the lane has no stored rows. So after holds nothing for it, the fingerprint covers only the lanes that were read, and the sync commits usable provenance for the source. After that:

  • lanes-index export --worktrees succeeds. It writes no line for the unread lane.
  • lanes --index --worktrees <lane> says "no worktree is inventoried" (exit 8).
  • lanes --worktrees <lane>, which reads the source and not the index, says the inventory was NOT READ.

The result is that a source read that failed shows up in the index as an empty answer.

A related gap with the same cause. A held lane that does have earlier rows keeps them. But the index doesn't record that the lane was held at the last sync, so an index read can't tell those rows apart from fresh ones. The synced <UTC> stamp in the read: line covers the whole source, not each lane.

Why it's low severity. Amendment 14(b) says no act reads the index, so nothing gates on this answer. Reading the source (lanes --worktrees, lanes-edit.sh worktrees, lane-reconcile) reports the lane correctly. The wrong answer shows up only on an --index read.

Two possible fixes

  1. Persist the unread state. Store a per-lane held/unread marker as a worktrees row (for example a row with an empty path and branch = unreadable inventory (<why>)) or as a provenance note. Export emits it, and the --index --worktrees render shows the same NOT READ line as the source read. This also fixes the related gap above.
  2. Withhold usable provenance. When a sync holds a lane that has no earlier rows, the sync doesn't commit provenance. The source stays unsynced (read: sources, which is the documented fallback) until a sync reads every lane.

Option 1 keeps --index useful on a workstation with one broken lane. Option 2 is smaller.

Tests to add

  • A first sync with one lane whose trees/ exists but is mode 000. Assert that the --index read does not answer "no worktree is inventoried" for that lane.
  • A later sync with the same lane held over earlier rows. Assert that the --index read says the rows are held.

Refs #161 #171 #97

Lane: openRepoTools-3

🤖 Generated with Claude Code

Activity

  1. brettheap commented on Oct 6, 2026

    @brettheap
    ContributorAuthor

    Copilot's fourth review of #171 (merge head 9b2ec40): four more findings

    Copilot reviewed the PR again after the #121 merge was pushed. That is past the two-round cap, so its findings go here and this stays the single follow-up issue for #171. None of the four touches the merge itself. Each one is in code that #171 adds.

    1. A partial lane listing can delete indexed rows. Thread: Derived index: a worktrees table mirroring every lane's #97 inventory under inventory:<Workstation>, nudged by every inventory write, and lanes --worktrees to read it with or without --index #171 (comment) (lanes-edit.sh worktree_rows, near line 13069 at 9b2ec40). lanes_rows is best-effort by design. When the archive can't be read, it notes that and returns 0 with the RETIRED rows missing (around lanes-edit.sh:7225). worktree_rows treats that partial set as complete. An archived lane that still has sidecars is left out, and lanes-index sync --source inventory then removes its indexed rows. The fix is a strict completeness signal for this read, for example an UNREAD marker per source when the archive could not be read, so that the indexer holds and does not delete.
    2. An inventory source is validated only after the lock is taken. Thread: Derived index: a worktrees table mirroring every lane's #97 inventory under inventory:<Workstation>, nudged by every inventory write, and lanes --worktrees to read it with or without --index #171 (comment) (lanes-index cmd_sync, near line 2049). While another sync holds the lock, sync --source inventory:Raven on Eagle (or --source inventory in an unnamed container) writes a generic inventory mark and exits 0 without refusing. The holder then syncs the local inventory. The fix is to run inventory_source(requested) before mark_rerun.
    3. SQLite read errors escape as a traceback. Thread: Derived index: a worktrees table mirroring every lane's #97 inventory under inventory:<Workstation>, nudged by every inventory write, and lanes --worktrees to read it with or without --index #171 (comment) (lanes-index near line 2202 in cmd_status, and near line 2328 in _export_worktrees). SqliteStore.rows() lets sqlite3.Error through. If the provenance is valid but the worktrees table is missing or damaged, status prints a traceback and never reaches its rows: unread line. The export also fails with a traceback, though lanes --index --worktrees still falls back to reading the sources. The fix is to translate sqlite3.Error to StoreUnusable in SqliteStore.rows.
    4. The footer's remedy names a command that reads only this workstation. Thread: Derived index: a worktrees table mirroring every lane's #97 inventory under inventory:<Workstation>, nudged by every inventory write, and lanes --worktrees to read it with or without --index #171 (comment) (lanes, near line 368). An index listing can show Eagle's rows when run on Raven, but lane-reconcile reads only the local sidecars and disk. The footer should say to run it on the row's workstation.

    Tests to add

    • An unreadable archive holding a lane with sidecars: a sync keeps that lane's rows.
    • A held lock with a foreign source: sync --source inventory:<other> refuses with exit 2.
    • A worktrees table dropped under valid provenance: status prints rows: unread, and export --worktrees exits through the unusable path.
    • The lanes --index --worktrees footer names the workstation.

    Lane: openRepoTools-3

    🤖 Generated with Claude Code

  2. changed the title [-]lanes-index inventory: a first sync that cannot read one lane publishes an empty, valid-looking indexed answer (Copilot round 3 on #171)[/-] [+]lanes-index inventory follow-ups from Copilot rounds 3 and 4 on #171: unread-lane answers, partial listings, foreign-source marks, SQLite read errors, footer remedy[/+] on Oct 6, 2026
  3. brettheap commented on Oct 6, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openRepoTools-3, session 83619980-06bb-4469-ae73-2a8c5e61981a@Eagle, 2026-10-06T20:42:04Z, for #173

    Logged in lanes/log/openRepoTools-3.md at b983d9ccf850553ee8eb43fc9e227d5e032d107a.

    The three reads (lane-collision-protocol Rule 1):

    1. existing `CLAIMED —` comments on opensoft/openRepoTools#173:
       none
    2. `gh pr list --repo opensoft/openRepoTools --state all --search "#173"`, kept where the row or its body names `#173`:
       none
    3. `git ls-remote --heads git@github.com:opensoft/openRepoTools` branches naming `173` as a whole token:
       none
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions