Description
next-prop-id.ts computes the next proposal number as max(existing NNN) + 1 by scanning proposals/ at call time. Two proposal-creation calls running concurrently (e.g. two background/worktree sessions, or a foreground session racing a routine) with different titles will both read the same max, compute the same NNN, produce different slugs, and both writes succeed — because the filename differs by slug, nothing detects the collision. Result: two proposal files sharing the same NNN (e.g. two PROP-014-*.md files with different content).
This is a race in the existing next-prop-id.ts → file-write flow and is not new; it is called out explicitly here because a related change hardens the same-name collision case (exclusive/atomic file creation) without addressing this cross-title variant.
Impact
Low likelihood (requires genuinely concurrent creates), but confusing when it happens: proposal IDs are meant to be unique identifiers referenced in cross-links, metrics, and operator commands (/proposal-act accept PROP-014), and ambiguity there is a real papercut.
Possible directions
- A locking or claim-then-verify step around NNN assignment (e.g. reserve the number by creating a placeholder file first, atomically).
- Switch the numbering scheme to something collision-proof by construction (e.g. timestamp-first ordering) rather than a scanned max+1.
Not scoped here — needs its own design pass.
Description
next-prop-id.tscomputes the next proposal number asmax(existing NNN) + 1by scanningproposals/at call time. Two proposal-creation calls running concurrently (e.g. two background/worktree sessions, or a foreground session racing a routine) with different titles will both read the same max, compute the same NNN, produce different slugs, and both writes succeed — because the filename differs by slug, nothing detects the collision. Result: two proposal files sharing the sameNNN(e.g. twoPROP-014-*.mdfiles with different content).This is a race in the existing
next-prop-id.ts→ file-write flow and is not new; it is called out explicitly here because a related change hardens the same-name collision case (exclusive/atomic file creation) without addressing this cross-title variant.Impact
Low likelihood (requires genuinely concurrent creates), but confusing when it happens: proposal IDs are meant to be unique identifiers referenced in cross-links, metrics, and operator commands (
/proposal-act accept PROP-014), and ambiguity there is a real papercut.Possible directions
Not scoped here — needs its own design pass.