Summary
Dream promotion uses the debounce timer as its only in-flight guard. Once the timer fires, scanDiary() can still be awaiting file IO or the promote_dream_entries RPC. A watcher event during that window can schedule another scan because state.timer has already been cleared.
Impact
The same diary content can be promoted concurrently or duplicated if a file change event arrives while the previous promotion RPC is still in flight.
Scope
This is a watcher/concurrency bug in dream promotion. It is independent of markdown ingestion and predictive context behavior.
Fix Path
Add an explicit scan-in-progress flag. Mark refreshes dirty while a scan is active, then run one serialized follow-up scan after the active scan finishes.
Verification Target
- Unit/integration test where refresh events arrive during an unresolved promotion RPC.
- Assert only one active promotion RPC runs at a time and a dirty follow-up runs afterward.
A previous broad PR (#218) included this but was closed for bundling unrelated changes. Bring this back as a focused dream-promotion patch.
– Vale
Summary
Dream promotion uses the debounce timer as its only in-flight guard. Once the timer fires,
scanDiary()can still be awaiting file IO or thepromote_dream_entriesRPC. A watcher event during that window can schedule another scan becausestate.timerhas already been cleared.Impact
The same diary content can be promoted concurrently or duplicated if a file change event arrives while the previous promotion RPC is still in flight.
Scope
This is a watcher/concurrency bug in dream promotion. It is independent of markdown ingestion and predictive context behavior.
Fix Path
Add an explicit scan-in-progress flag. Mark refreshes dirty while a scan is active, then run one serialized follow-up scan after the active scan finishes.
Verification Target
A previous broad PR (#218) included this but was closed for bundling unrelated changes. Bring this back as a focused dream-promotion patch.
– Vale