Skip to content

Share workspace inventory across windows - #226

Open
wesm wants to merge 48 commits into
mainfrom
kenn-forge/issue-186-shared-inventory
Open

Share workspace inventory across windows#226
wesm wants to merge 48 commits into
mainfrom
kenn-forge/issue-186-shared-inventory

Conversation

@wesm

@wesm wesm commented Sep 1, 2026

Copy link
Copy Markdown
Member

Worktrees and tmux sessions created or removed outside Ghosthub now appear in every window without relaunching the app.

  • One application-wide inventory store replaces per-window polling. It coalesces reads per host, refreshes every 30 seconds while the app is active, refreshes immediately on reactivation, and lets later windows reuse cached results instead of repeating host commands.
  • KWT and tmux refreshes stay independent. A failed refresh keeps the last rows, revokes freshness, and reports the existing host warning.
  • The store owns the reload after a worktree or project mutation. Post-mutation results are fenced by the endpoint and a per-host mutation epoch captured when the mutation starts, so a slow result from an earlier mutation or a replaced endpoint cannot overwrite newer inventory. Only a warning-free result satisfies the fence; anything else, including a project registered while a mutation is in flight, gets one fence-end reload.
  • Inventory loaded before a mutation completes, such as removal preflight, stays in the mutating window. Scene-local tmux probes publish to the store only when no newer shared refresh has started, and yield absence results otherwise.
  • Removal tombstones hide removed worktrees and projects in every window until a fresh load confirms they are gone, including under a legacy-empty repository identity and in inventory a window loads for its own mutation. Re-registering a project clears its tombstones everywhere.
  • Herdr and Zellij inventory remain scene-owned.

Closes #180
Closes #184
Closes #186

External KWT worktree changes and tmux session changes must appear without an
app relaunch. Per-window discovery left stale rows and repeated the same host
commands for every open scene.

Use one application cache and an active-only cadence so every window consumes
the same authoritative results. Keep direct mutation preflights, remote helper
provisioning, tombstones, and freshness guards intact.

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 01:16 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 01:16 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 01:17 — with GitHub Actions Active
@roborev-ci

roborev-ci Bot commented Sep 1, 2026

Copy link
Copy Markdown

roborev: Combined Review (fafc4e5)

Verdict: Three distinct medium-severity inventory-sharing issues remain; no high or critical findings were reported.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:421-432 — Cancelling the last subscriber’s in-flight load removes the task but leaves the entry in .loading. Later subscribers may skip the initial load until an explicit refresh or 30-second cadence. Reset invalidated entries to .idle, or treat .loading entries without an active task as needing a load.

  • Sources/App/WorkspaceInventoryStore.swift:360-371 — Failed-project worktree retention is applied only in each scene’s local cache, while the shared cache stores raw warning-bearing KWT results. Newly opened windows may lose previously visible worktrees after partial inventory failures. Apply retention when updating the shared cache or share the reconciled inventory.

  • Sources/App/WorkspaceSceneModel.swift:5064-5067 — Mutations apply excludingWorktrees locally but publish the unfiltered inventory to the shared store. Later windows can reintroduce deleted worktrees until the next backend refresh. Publish the post-exclusion inventory or propagate removal tombstones.


Reviewers: 2 done | Synthesis: codex, 8s | Total: 13m54s

Later windows must see the same reconciled inventory as the window that
performed a refresh or mutation. Raw partial results and cancelled loads could
otherwise hide valid worktrees, stall discovery, or restore a removed row.

Preserve failed-project rows in the shared cache, carry removal exclusions into
authoritative publication, and let a new subscriber restart cancelled loads.

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 13:15 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 13:15 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 13:15 — with GitHub Actions Active
@wesm

wesm commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

Addressed all three medium findings in 4abd92e6:

  • Cancelled last-subscriber KWT and tmux loads now return to idle, so a later window starts a new load immediately.
  • Warning-bearing partial KWT results now retain failed-project worktrees in the shared cache.
  • Worktree-removal exclusions now reach authoritative shared-cache publication, so a later window cannot restore a removed row.

Each path has a focused regression. The focused inventory suites, essential workflows, formatting, and app build pass. The full 1,975-test run had one unrelated tmux-recovery timing failure; all three cases in that test passed on immediate isolated rerun.

@roborev-ci

roborev-ci Bot commented Sep 1, 2026

Copy link
Copy Markdown

roborev: Combined Review (4abd92e)

Verdict: Three medium-severity consistency issues were identified; no critical or high-severity findings.

Medium

  • Sources/App/WorkspaceSceneModel.swift:5061-5081 — Shared KWT publication suppresses the subscriber callback and advances the applied revision only for the publishing hostID. Scene aliases resolving to the same CommandHost can remain stale. Reconsume the snapshot for all aliases or update every matching host identity.

  • Sources/App/WorkspaceInventoryStore.swift:202-218, 365-380 — Removal exclusions apply only to the individual publication and are not retained in the shared cache. Later stale refreshes can reintroduce removed worktrees for scenes without their own tombstone. Persist shared removal tombstones/exclusions until the authoritative inventory no longer contains the identity.

  • Sources/App/WorkspaceInventoryStore.swift:210-212, 496-511 — One mutation’s publication can mark the current host-wide fence generation satisfied for concurrent mutations on the same host. A later mutation may skip its fence-end refresh, leaving the cache stale. Track fence satisfaction per mutation scope or only satisfy a fence when no other scope was active during publication.


Reviewers: 2 done | Synthesis: codex, 8s | Total: 13m27s

Concurrent window mutations could leave endpoint aliases or later subscribers
with stale worktrees. One mutation publication could also satisfy another
mutation's fence before the shared cache reconciled the endpoint.

Keep removal identities in the shared cache until complete inventory confirms
their absence. Reconsume mutation publications for every endpoint alias, and
reserve fence satisfaction for a sole active endpoint mutation. Use the
concurrency-aware wait for the Herdr recovery check that timed out in CI.

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 14:58 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 14:58 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 14:58 — with GitHub Actions Active
@roborev-ci

roborev-ci Bot commented Sep 1, 2026

Copy link
Copy Markdown

roborev: Combined Review (334876e)

Verdict: One medium-severity cache consistency issue requires attention.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:556-570.ended mutation events ignore removalTombstones and removesProject. Checkout-already-absent or post-refresh-failure paths can refresh the shared cache without exclusions, allowing stale worktrees or deleted projects to reappear. Propagate worktree and project tombstones into the shared store and retain them until authoritative inventory confirms removal.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 15m53s

A fence-end refresh could restore a worktree or project that a mutation had
already removed when the host returned stale inventory. A quarantined removal
could also consume a pre-mutation cache entry as fresh authority.

Keep unresolved removal identities in the shared cache until complete
inventory confirms their absence. Treat quarantined cache entries as stale so
only a post-quarantine load can release suppression.

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 20:42 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 20:42 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 1, 2026 20:43 — with GitHub Actions Active
@roborev-ci

roborev-ci Bot commented Sep 1, 2026

Copy link
Copy Markdown

roborev: Combined Review (a5ec3bb)

Verdict: Three medium-severity refresh and cache-consistency issues were identified; no concrete security vulnerability was found.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:185-198,301-370; Sources/App/WorkspaceSceneModel.swift:10740 — Explicit refreshes are ignored while a lane is loading, allowing stale in-flight tmux results to republish killed sessions. Force refreshes should advance the generation, cancel the existing task, and start a replacement load.

  • Sources/App/WorkspaceInventoryStore.swift:172-183,279-298 — Removing the last subscriber does not invalidate completed or failed cache entries. A later window may display stale inventory for up to one cadence interval. Track subscriber transitions and refresh when the count changes from zero to one.

  • Sources/App/WorkspaceInventoryStore.swift:496-529 — Mutation-end tombstones are applied only after a subsequent successful inventory load, allowing removed rows to remain visible or reappear when reconciliation is delayed, cancelled, or fails. Apply tombstones immediately to the cached inventory and preserve the filtered view across failed refreshes.


Reviewers: 2 done | Synthesis: codex, 7s | Total: 16m2s

A user refresh or session kill must not wait behind a result captured before
the action. A window opened after all subscribers closed must also recheck the
host instead of trusting an inactive cache.

Publish known removals to the shared view immediately. Keep that filtered view
when host reconciliation is delayed or fails.

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
@wesm
wesm deployed to sandbox-image-promotion-status September 2, 2026 00:44 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 2, 2026 00:44 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 2, 2026 00:44 — with GitHub Actions Active
@roborev-ci

roborev-ci Bot commented Sep 2, 2026

Copy link
Copy Markdown

roborev: Combined Review (81ed1f7)

Verdict: Two medium-severity issues require attention.

Medium

  • Sources/App/WorkspaceSceneModel.swift:4063-4081, Sources/App/WorkspaceInventoryStore.swift:624-642 — Tombstone updates increment inventoryRevision without updating the shared entry’s failure or observation state. Reapplying the cached inventory can clear a scene’s local KWT failure even though the shared entry remains failed. Preserve or reapply the failure state unless the entry is freshly and successfully loaded.

  • Sources/App/WorkspaceInventoryStore.swift:240-247, 303-304, 351-352 — Reactivation requests are skipped when KWT or tmux tasks are already running. Slow tasks started while inactive may publish stale results, delaying refresh until the next 30-second cadence. Cancel and invalidate active tasks on reactivation, or queue an immediate refresh after completion.

No additional issues were identified by the second reviewer.


Reviewers: 2 done | Synthesis: codex, 7s | Total: 14m44s

Inventory work that began before Ghosthub became inactive can miss host
changes made while the app is in the background. Replace that work when the
app becomes active so the shared cache reflects the current host state.

Cached tombstone filtering is not a successful host load. Preserve an existing
KWT failure until a fresh load succeeds, while still hiding known removals.

Generated with Codex

Co-authored-by: Codex <codex@openai.com>
@wesm
wesm deployed to sandbox-image-promotion-status September 2, 2026 01:24 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 2, 2026 01:24 — with GitHub Actions Active
@wesm
wesm deployed to sandbox-image-promotion-status September 2, 2026 01:25 — with GitHub Actions Active
@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (ae336af)

Verdict: One medium-severity issue found; otherwise, no additional issues were reported.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:808-810 — Registering any project clears all worktree-removal tombstones under the legacy empty repository identity (""). With multiple legacy-empty projects, this can release another project’s tombstones and cause removed worktrees to reappear. Associate legacy-empty tombstones with the project path and clear only those belonging to the registered project.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 12m43s

Worktree removals from a legacy-empty project are now recorded under
the project's normalized path instead of the empty identity, and the
worktree removal release carries that path. Registration therefore
releases only the registered project's tombstones instead of every
legacy tombstone on the host.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (e05ea9f)

Verdict: Three medium-severity correctness issues remain; no security vulnerabilities were identified.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:403-421
    If provisioning succeeds after cancellation or generation invalidation, the task still runs the KWT loader, performing stale inventory work before discarding the result. Recheck cancellation and request generation before starting the loader.

  • Sources/App/WorkspaceInventoryStore.swift:798-819; Sources/App/WorkspaceSceneModel.swift:4295-4303
    Project registration does not clear worktree-only tombstones associated with previous repository identities, which can continue hiding restored worktrees after an identity change. Clear tombstones by registered project path across all repository identities in both shared and scene-local state.

  • Sources/App/WorkspaceSceneModel.swift:4222-4254
    Refresh progress ignores entry.isFresh. A provisional publication can remain .loaded while freshness is revoked, causing premature refresh completion and ready state. Treat non-fresh entries as pending or introduce a distinct provisional state.


Reviewers: 2 done | Synthesis: codex, 9s | Total: 21m30s

… pending

A KWT request rechecks cancellation and generation after provisioning
so an invalidated request does not run the loader. The store remembers
the project path behind each worktree tombstone key and a registration
releases every key recorded at the registered path, covering earlier
repository identities. Scene-local worktree tombstones are removed;
they only re-filtered inventory the store had already filtered. A
provisional cache entry now counts as pending in refresh progress.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Sep 4, 2026

Copy link
Copy Markdown

roborev: Combined Review (15efe28)

Verdict: Two medium-severity issues were identified.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:376-387 — A provisional KWT publication clears freshness while preserving a non-idle state. Later subscribers may skip the initial load, leaving the UI stale or stuck loading until the next cadence refresh. Treat any non-fresh entry as requiring a load; the existing task guard prevents duplicate requests.

  • Sources/App/WorkspaceInventoryStore.swift:81-95, 577-585 — Project-removal tombstones identify canonical projects only by repository. If the repository is re-registered at a new path or with a new registration fingerprint, authoritative KWT results still match the old tombstone and keep the project hidden unless a local .registered event occurs. Store and compare the registration fingerprint, and path where needed, so new registrations clear old tombstones.


Reviewers: 2 done | Synthesis: codex, 6s | Total: 21m59s

…a registration

A new subscriber now triggers a load whenever the cached entry is not
fresh, including after a provisional publication. A project removal
tombstone records the removed registration's path and fingerprint, so a
repository registered again at another path or with a new fingerprint
is no longer hidden as if it were the removed project.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Sep 4, 2026

Copy link
Copy Markdown

roborev: Combined Review (935fcf7)

Verdict: Two medium-severity tombstone matching issues require attention; no critical or high-severity security issues were found.

Medium

  • Removal tombstones mishandle Windows paths
    Location: Sources/App/KwtInventoryClient.swift:947-975; Sources/App/WorkspaceInventoryStore.swift:561-568
    Paths are recognized only when beginning with /, and normalization does not support Windows drive-letter paths or separators. Paths such as C:\code\repo may be treated as repository IDs, causing tombstones to be dropped or misapplied during partial loads.
    Fix: Store tombstone keys with an explicit repository/path discriminator and use platform-aware path normalization.

  • Project registration clears tombstones too broadly
    Location: Sources/App/WorkspaceInventoryStore.swift:839-857
    Registering any project with a matching repository clears all removal tombstones for that repository, even when the project path differs. This can remove another project’s fence and allow stale rows or worktrees to reappear.
    Fix: Clear tombstones only when both the recorded path and identity match.


Reviewers: 2 done | Synthesis: codex, 8s | Total: 23m42s

Path keys for worktree tombstones now carry an explicit marker instead
of relying on a leading slash, so no repository identity or Windows
path can be mistaken for one. Registration releases only tombstones
recorded at the registered path, or repository-keyed ones with no
recorded path, so the same repository registered elsewhere keeps its
fence. The clearing logic is plain statements, which also fixes the
release build's type-check timeout on the previous expression.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Sep 4, 2026

Copy link
Copy Markdown

roborev: Combined Review (64a27de)

Verdict: Two medium-severity issues identified.

Medium

  • Project registration does not refresh tmux inventory
    Location: Sources/App/WorkspaceSceneModel.swift:6984; Sources/App/WorkspaceInventoryStore.swift:795-817
    Project registration only invalidates KWT inventory, so sessions for the newly registered project may remain missing until a manual refresh or cadence interval. Invalidate and request tmux inventory for affected hosts when handling .registered.

  • Legacy project removals can be resurrected
    Location: Sources/App/WorkspaceSceneModel.swift:3056-3064, 3181-3184
    Worktree-removal exclusions use project.scopedKey, which is empty for legacy projects. If KWT later returns a canonical non-empty identity, the exclusion no longer matches and the removed worktree may be republished. Use a path-based tombstone key for projects without an identity, or canonicalize exclusion keys before publication.


Reviewers: 2 done | Synthesis: codex, 11s | Total: 20m36s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Sep 4, 2026

Copy link
Copy Markdown

roborev: Combined Review (f1b778f)

Verdict: One medium-severity retry/cadence issue requires attention; no security vulnerabilities were identified.

  • MediumSources/App/WorkspaceInventoryStore.swift:221-225, 391-400; Sources/App/WorkspaceSceneModel.swift:3850-3852
    Every scene snapshot change re-registers the same subscriber. When KWT is failed, provisional, or quarantined, this triggers immediate retries outside the 30-second cadence and may repeatedly reprovision remote hosts. Only trigger initial loads for newly added subscribers or host lanes; handle retries through explicit refreshes, mutation completion, or a backoff-aware cadence.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 21m0s

Scenes re-register their subscriber on every snapshot change. Since a
stale entry counts as needing an initial load, that re-registration
retried failed or provisional hosts outside the cadence. Only a host or
lane the subscriber did not have before triggers an initial load.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Sep 4, 2026

Copy link
Copy Markdown

roborev: Combined Review (d39f24b)

Verdict: Two medium-severity issues found in project tombstone and removal handling.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:166, 769-772 — Only one path is stored per repository tombstone key. A later removal at another path overwrites the earlier path, preventing re-registration from clearing its tombstones and leaving restored worktrees hidden. Track paths per tombstone identity or key tombstones by repository and normalized project path.

  • Sources/App/WorkspaceSceneModel.swift:4366-4369 — Project removal ignores projectPath when repository identities are present, removing every project with the same repository identity, including registrations at other paths that should remain. Use path- and fingerprint-aware matching consistent with shared project tombstones.


Reviewers: 2 done | Synthesis: codex, 8s | Total: 18m8s

A worktree belongs to exactly one registration, so its removal
tombstone is now keyed by the project's path whenever the path is
known. That replaces the single path recorded per repository key, which
a later removal at another path overwrote. A completed project removal
now also requires the removed path when both identities are present,
so the same repository registered elsewhere stays.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Sep 4, 2026

Copy link
Copy Markdown

roborev: Combined Review (bc60049)

Verdict: Medium-severity path-handling issues remain for duplicate repository registrations.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:759-769 — Removal tombstones select the first cached project for a repository instead of matching event.projectPath, which can produce the wrong fingerprint and fail to hide the removed path during stale refreshes. Match by normalized project path and add a duplicate-repository regression test.

  • Sources/App/WorkspaceSceneModel.swift:5161-5184 — Quarantined project-removal recovery matches surviving projects by repository only, so a project registered at another path may be mistaken for the removed project. Require normalized project-path equality when resolving removals and restoration targets.

  • Sources/App/KwtInventoryClient.swift:816-834 — Snapshot merging prefers the first project with the same repository before checking paths, allowing duplicate registrations to inherit the wrong UUID and destabilize selections or sessions. Match by normalized path first, using repository-only fallback only when unambiguous.


Reviewers: 2 done | Synthesis: codex, 7s | Total: 26m17s

wesm and others added 4 commits September 4, 2026 14:43
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A healthy record of the same repository at another path no longer
restores a quarantined removal; the removal completes instead. A
warned record still holds the quarantine only at the quarantined path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Sep 4, 2026

Copy link
Copy Markdown

roborev: Combined Review (51d67a1)

Verdict: One medium-severity issue found; otherwise no critical or high-severity concerns.

Medium

  • Duplicate KWT refreshes for aliased hostsSources/App/WorkspaceSceneModel.swift:6949, Sources/App/WorkspaceInventoryStore.swift:802
    Project registration emits separate .registered events for host aliases sharing the same CommandHost. The store processes each independently, repeatedly restarting KWT loads; canceled tasks may still launch detached loaders, causing duplicate provisioning and discovery work. Deduplicate notifications by resolved CommandHost or make event handling idempotent.

Reviewers: 2 done | Synthesis: codex, 8s | Total: 18m33s

wesm and others added 2 commits September 4, 2026 16:02
Host aliases resolving to one command host emitted one registration
event each, and every event restarted the KWT load the previous one
had started. The scene now announces once, and the store drops the
satisfied fence for every host identity aliasing the affected hosts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Sep 4, 2026

Copy link
Copy Markdown

roborev: Combined Review (56e404a)

Verdict: One medium-severity issue requires attention; no critical or high-severity findings were reported.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:613-617, 776-781 — Quarantined project-removal scopes remain in mutationCoordinator.scopes, potentially blocking authoritative mutation publication and causing the .ended handler to skip the KWT reload needed to resolve the quarantine. Exclude quarantinedProjectRemovals.keys from active-mutation checks via a shared non-quarantined-scope helper.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 18m36s

wesm and others added 2 commits September 4, 2026 17:08
A quarantined project removal stays registered with the coordinator
until inventory resolves it. The initial-load fence already ignored
such scopes; fence satisfaction and the mutation-end reload guard now
share that rule, so a parked quarantine no longer blocks another
mutation's publication or its fence-end reload.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@roborev-ci

roborev-ci Bot commented Sep 4, 2026

Copy link
Copy Markdown

roborev: Combined Review (b75565d)

Verdict: One medium-severity issue found; otherwise no concrete security regressions identified.

Medium

  • Sources/App/WorkspaceSceneModel.swift:6951-6954 — Project registration is silently dropped for unsaved SSH host drafts without a matching inventoryHosts entry. The shared cache is not refreshed or cleared of removal tombstones, so newly registered projects may remain invisible. Publish registration by the target CommandHost, including unsaved drafts, or force the shared endpoint to clear matching tombstones and reload KWT inventory.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 13m36s

Host Settings can register a project before its SSH host is saved. Without
a matching scene inventory host, registration left the shared cache and
removal tombstones untouched, keeping the project hidden.

Let draft registrations clear the shared endpoint's tombstones and reload
its inventory, including when no scene currently subscribes to that host.

Generated with Codex
Co-authored-by: Codex <codex@openai.com>
@roborev-ci

roborev-ci Bot commented Sep 5, 2026

Copy link
Copy Markdown

roborev: Combined Review (acee486)

Verdict: One medium-severity inventory tombstone issue requires attention.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:103-108 — When the shared cache lacks a project, its removal tombstone has an empty registration fingerprint. Matching treats this as a wildcard, so an externally re-registered project at the same repository/path may remain hidden indefinitely until Ghosthub explicitly registers it again. Treat a later non-empty authoritative fingerprint as replacement evidence and clear the unknown-fingerprint tombstone.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 19m50s

A removal can finish before the shared cache knows the project's
registration fingerprint. Treating that missing fingerprint as a permanent
wildcard hid projects registered again outside Ghosthub at the same path.

Let an authoritative inventory record with a non-empty fingerprint retire
the unknown tombstone. Provisional results still leave the removal in place.

Generated with Codex
Co-authored-by: Codex <codex@openai.com>
@roborev-ci

roborev-ci Bot commented Sep 5, 2026

Copy link
Copy Markdown

roborev: Combined Review (c051f83)

Verdict: Medium-severity issues found; no Critical or High-severity findings.

Medium

  • Sources/App/WorkspaceInventoryStore.swift:553-605 — Re-registering a project with a new fingerprint releases its project tombstone, but associated path-keyed worktree tombstones remain active, leaving existing worktrees hidden indefinitely. Clear or re-identify those worktree tombstones when authoritative registration supersedes an unknown-fingerprint removal.

  • Sources/App/WorkspaceSceneModel.swift:3780-3795 — PR-import fallback publishes reconstructed inventory excluding synthesized projects even when KWT refresh fails, removing unrelated orphan worktree rows from the scene and shared cache. Preserve synthesized rows or retain the prior snapshot-only merge when refresh is provisional.

  • Sources/App/WorkspaceInventoryStore.swift:731-790 — Mutation-end handling recomputes command hosts from current subscribers. If the mutating scene or host disappears before completion, no host is reloaded or tombstoned, leaving remaining aliased windows stale. Persist the command-host mapping when mutation begins and use it for end-of-mutation publication.

Reviewers found no concrete security vulnerabilities.


Reviewers: 2 done | Synthesis: codex, 9s | Total: 31m13s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Worktrees removed via external kwt remove stay in the sidebar until app relaunch Refresh externally created sessions detect new worktrees

1 participant