You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A manual /dev-loop:knowledge-flush and the auto-flush.sh hook drained the same
queue into two competing PRs, and the hook's run reset the manual run's working
tree out from under it.
Timeline (2026-08-11 → 08-12, one machine, one queue):
16:02 — manual flush follows the skill: ~/.dev-loop/repo → git checkout main && git reset --hard origin/main, branch knowledge/…-20260811-160220, ingests 2
of the 6 pending candidates, commits, pushes.
16:21 — hooks/auto-flush.sh fires (pid 38521) and starts a headless claude -p "Run the dev-loop:knowledge-flush skill now…" (pid 38522).
That session runs the same step 1 against the same path, so ~/.dev-loop/repo is checked out to main and hard-reset. The manual run's
working tree (2 new pages, 2 index edits, log.md, INGEST_REPORT.md) is gone
mid-run. Only the fact that it had already committed and pushed kept the work.
The queue's .processed.jsonl retire step (step 5) is also racy: whichever run
finishes first retires candidates the other run is still mid-ingest on.
Contributing factors
~/.dev-loop/repo is a single fixed path baked into the skill's step 1, and
step 1 begins with a destructive reset --hard. Any second run is a data hazard.
~/.dev-loop/.autoflush.lock exists, but it is taken by the hook path only.
A manual /dev-loop:knowledge-flush neither acquires nor respects it, so the lock
cannot serialize the two entry points that actually collide.
The manual entry point has no way to detect the hook's run: the hook is detached
(nohup-style, reparented to pid 1), so the manual session sees no child process
and no marker in the repo it is about to reset.
Possible directions
[추정] — none of these are tested; listing them so the tradeoff is visible rather
than proposing one as correct:
Move the lock up to the skill. Step 1 acquires ~/.dev-loop/.flush.lock
(both entry points), and the hook simply doesn't fire when it is held. Smallest
change; makes the two entry points mutually exclusive.
Per-run checkout.~/.dev-loop/repo-<runid> or a git worktree per flush,
so a second run cannot touch the first's tree. Costs disk and a clone per run,
but removes the hazard rather than serializing around it.
Claim the queue rows. Mark candidates in-progress with a run id at the
start and skip claimed rows, so even a concurrent pair produces disjoint PRs.
Addresses the expensive half (duplicate ingestion) even if trees still collide.
(1) and (3) are complementary: the lock prevents the collision, the claim bounds the
damage when the lock is bypassed or a run dies holding it.
Environment
dev-loop plugin 1.6.0 (installed via marketplace), macOS 25.5.0
Both runs used the same $HOME, so the same ~/.dev-loop/queue/ and ~/.dev-loop/repo
No data was lost (the manual run had pushed); the report is about the hazard, not
a recovery request
What happened
A manual
/dev-loop:knowledge-flushand theauto-flush.shhook drained the samequeue into two competing PRs, and the hook's run reset the manual run's working
tree out from under it.
Timeline (2026-08-11 → 08-12, one machine, one queue):
16:02— manual flush follows the skill:~/.dev-loop/repo→git checkout main && git reset --hard origin/main, branchknowledge/…-20260811-160220, ingests 2of the 6 pending candidates, commits, pushes.
16:21—hooks/auto-flush.shfires (pid 38521) and starts a headlessclaude -p "Run the dev-loop:knowledge-flush skill now…"(pid 38522).~/.dev-loop/repois checked out tomainand hard-reset. The manual run'sworking tree (2 new pages, 2 index edits,
log.md,INGEST_REPORT.md) is gonemid-run. Only the fact that it had already committed and pushed kept the work.
the manual run's branch was discarded by hand after comparing them.
Why it matters beyond one lost working tree
researched, deduped, routed and wrote pages for the same six insights. knowledge: signed-link verification assertions, cross-module consumer census, defect-class re-sweep, synced-cluster pod triage, cloud CLI bounds (5 ingested, 1 dropped as dup of #52) #76 and the
discarded branch created pages for the same candidates at different paths
(
failing-pod-on-a-repo-synced-clustervsworkload-startup-failure-triage,cloud-cli-invocation-boundsvscloud-cli-query-scoping).warns about ("Two real pile-ups (knowledge: keg-only PATH + double-quote backslash (2 verified platforms insights) #17–knowledge: 5 verified insights — git-status -uall purity gates, override control pairs, guard-true coverage, per-channel masking verification, lowered-declaration survival #40, then knowledge: gate-author quoting, sh-invoked stubs, stderr-warning hooks, vacuous-green tests, temp-leak attribution, throttled auth calls #42/knowledge: ESM DI spawn tripwire + independent GitHub credential channels (2 merged, 1 dup dropped) #43 — see wiki-audit: knowledge pipeline hardening — gate bypass window, checkable dedup evidence, harvest caps #39) came from
skipping exactly this step"). The in-flight dedup step (2b′) can only compare
against PRs that already exist; it cannot see a sibling flush that is running
right now and has not pushed yet. Every concurrent pair is invisible to it by
construction.
.processed.jsonlretire step (step 5) is also racy: whichever runfinishes first retires candidates the other run is still mid-ingest on.
Contributing factors
~/.dev-loop/repois a single fixed path baked into the skill's step 1, andstep 1 begins with a destructive
reset --hard. Any second run is a data hazard.~/.dev-loop/.autoflush.lockexists, but it is taken by the hook path only.A manual
/dev-loop:knowledge-flushneither acquires nor respects it, so the lockcannot serialize the two entry points that actually collide.
(
nohup-style, reparented to pid 1), so the manual session sees no child processand no marker in the repo it is about to reset.
Possible directions
[추정]— none of these are tested; listing them so the tradeoff is visible ratherthan proposing one as correct:
~/.dev-loop/.flush.lock(both entry points), and the hook simply doesn't fire when it is held. Smallest
change; makes the two entry points mutually exclusive.
~/.dev-loop/repo-<runid>or agit worktreeper flush,so a second run cannot touch the first's tree. Costs disk and a clone per run,
but removes the hazard rather than serializing around it.
in-progresswith a run id at thestart and skip claimed rows, so even a concurrent pair produces disjoint PRs.
Addresses the expensive half (duplicate ingestion) even if trees still collide.
(1) and (3) are complementary: the lock prevents the collision, the claim bounds the
damage when the lock is bypassed or a run dies holding it.
Environment
$HOME, so the same~/.dev-loop/queue/and~/.dev-loop/repoa recovery request