Skip to content

The lifecycle's remaining fail-open reads: an unreadable register becomes 'no holder', an unreadable sidecar becomes no tree, and two git reads become fabricated values #113

Open

Description

@brettheap

Four reads in the crash-consistent lifecycle still convert a failure into an answer, which is the rule (R22, Amendment 7(d): a read that failed is never an answer) the capability on #97 exists to enforce. Each is one || : or one continue.

1. The holder ids — an unreadable register becomes "no holder" (lane_reconcile):

  lrc_ids="$( { session_ids_of_lane "$lrc_lane" 2>/dev/null || :
                session_ids_local_of_lane "$lrc_lane" 2>/dev/null || :; } | awk 'NF && !seen[$0]++')"

Both reads are swallowed, so a register or local-row read that FAILED comes out as an empty id set. live_holder answers 8 for an empty set — this lane records no session, nothing can be live — and the verdict table then reads RUNNING + 8 as ungraceful-stop and SWAPPING + 8 as interrupted-swap. A crash is pronounced on a liveness question nobody answered. live_holder's own contract already has three answers (0 a holder, 8 none, anything else the records could not be read) and lane_reconcile already maps its * to indeterminate; what is missing is the same distinction one read earlier.

2. An unreadable tree sidecar is dropped (lane_trees_list):

  for ltl_f in "$ltl_root"/trees/*.yaml; do
    [ -r "$ltl_f" ] || continue

An existing record nobody can read and a record that is not there go into the same silence. The tree it names can then no longer be classified missing or possible-loss — every stored observation of it is gone — and it can be rediscovered only as unmanaged; where it is the lane's ONLY sidecar the function returns 8, this lane owns no worktree, about an inventory nobody could read.

3. git config --get branch.<b>.remote is unguarded (lane_tree_now):

      ltn_cfg="$(git -C "$ltn_p" config --get "branch.$ltn_raw.merge" 2>/dev/null)" || ltn_crc=$?
      case "$ltn_crc" in 0|1) : ;; *) return 3 ;; esac
      if [ -n "$ltn_cfg" ]; then
        ltn_rem="$(git -C "$ltn_p" config --get "branch.$ltn_raw.remote" 2>/dev/null || :)"
        ltn_u="${ltn_rem:-.}/${ltn_cfg#refs/heads/}"

The merge read one line above asks git for its status and returns 3 for anything but found or not found; the remote read beside it does not. A read that failed becomes an empty remote, and ${ltn_rem:-.} then FABRICATES a local upstream — ./<merge> — which is filed in the sidecar as an observation, with unknown unpushed beside it.

4. git worktree list --porcelain is swallowed (lane_reconcile, the unmanaged/stale sweep):

  done <<EOF
$(git -C "$lrc_dir" worktree list --porcelain 2>/dev/null || :)
EOF

An owning checkout whose registrations cannot be read is treated exactly as one with no registrations: every stale registration and every unmanaged tree in it is omitted, while the report can still print a resumable verdict. The estate's own resume then fails on the stale registration the report said nothing about.

Why this is filed rather than fixed on #97

Copilot review rounds are capped at two per pull request (Brett Heap's ruling of 2026-09-16); #97 is at round six, and it took only the two findings of that round that are safety holes it could not land with — an unreadable lifecycle snapshot read as this lane has no snapshot, and set-lane-tree writing outside the lane mutex. These four are the same class and none is new to that branch: 1, 2 and 4 are unchanged since the capability's first commit, and 3 is the one read round 5's sweep of lane_tree_now did not reach.

What's open

Each wants its own distinction kept rather than a new mechanism: an id set that could not be read must not reach live_holder as an empty one; an unreadable sidecar wants a row of its own that lane-reconcile reports as indeterminate (its file name is tree_id_for of the path, so the id is knowable without reading it); the remote read wants the case its sibling already has; and the registration sweep wants the command's status captured, with a verdict that refuses to read as a clearance when the sweep could not be made.

Filed from Copilot round 6 on #97 — #97 (comment), #97 (comment), #97 (comment), #97 (comment) — and named in openspec/changes/add-crash-consistent-lane-worktree-recovery/tasks.md section 7.

Activity

  1. brettheap commented on Sep 16, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openRepoTools-3, session 4135b2c9-34a6-47e3-a156-5adebb632fae@Eagle, 2026-09-16T13:05:21Z, for #113

    Logged in lanes/log/openRepoTools-3.md at 7e42cff54cab9bf9b946e7544e3bcc9b224a101b.

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

    1. existing `CLAIMED —` comments on opensoft/openRepoTools#113:
       none
    2. `gh pr list --repo opensoft/openRepoTools --state all --search "#113"`, kept where the row or its body names `#113`:
       none
    3. `git ls-remote --heads git@github.com:opensoft/openRepoTools` branches naming `113` 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