Skip to content

feat(bin): detect duplicate work at spawn intake (CFVC-02) - #1979

Open
sbracewell64 wants to merge 5 commits into
kunchenguid:mainfrom
sbracewell64:fm/cfvc-02-duplicate-work-intake
Open

feat(bin): detect duplicate work at spawn intake (CFVC-02)#1979
sbracewell64 wants to merge 5 commits into
kunchenguid:mainfrom
sbracewell64:fm/cfvc-02-duplicate-work-intake

Conversation

@sbracewell64

Copy link
Copy Markdown

Intent

CFVC-02: detect duplicate work at intake, per data/cfvc-synthesis-and-plan/report.md section 4 (the CFVC-02 table there is the authoritative spec).

GOAL: a task cannot be dispatched onto work that an open task, a live branch, or an open PR already covers, without the overlap being surfaced first. Measured cost of the gap: two tasks, two branches, two workers, one fix, nothing detected - a recorded live incident.

OWNER, as the spec fixes it: bin/fm-spawn.sh, the existing last gate before dispatch - no new component. Also bin/fm-pr-lib.sh, which gains the shared open-pull-request title listing so the pull request source is not a second forge reader with its own failure semantics. CODE emits the candidate overlap set - open tasks sharing id/title/body tokens, local and remote branches, open PR titles - and dispatch requires the overlap to be acknowledged. Code NEVER decides equivalence; 'is this the same work' compares two prose descriptions and stays firstmate's judgment.

BINDING REQUIREMENTS FROM THE SPEC:

  1. Advisory-only for its first period, on the fm-model-registry-lib.sh additive-inert pattern ('Unenforced is never SILENT, but it is not a refusal'); removing the block restores current behaviour exactly.
  2. Fail loud, not open: an unreachable forge yields overlap=unavailable, NEVER overlap=none. An absent set is not a pass - the same law PR 1614 encodes for check runs.
  3. Red-capable test replaying the recorded incident: with merge-refuses-unverified-green open, dispatching merge-path-verifies-no-ci-green must surface the overlap. Negative control: an unrelated task surfaces no overlap.
  4. Completion criteria: (a) the replay test red before and green after; (b) a forge outage produces unavailable and not none; (c) dispatching against a live overlap requires explicit acknowledgement in the spawn record.
  5. Shared tracked material: firstmate-coding-guidelines loaded and followed.

HOW REQUIREMENTS 1 AND 4c WERE RECONCILED - deliberate, not an oversight. The scan ALWAYS runs, always emits, is never silent, and config/spawn-overlap selects the posture. Absent or 'advisory' (default) prints and records but never refuses - the additive-inert first period, existing spawns behave as before. 'enforce' additionally requires --overlap-ack to name every surfaced ref, which is criterion (c). 'off' skips the scan and says so. Any other value, and a present-but-unreadable file, REFUSE, because a broken safety knob must never read as an absent one.

DELIBERATE DESIGN DECISION THE DIFF ALONE WILL NOT EXPLAIN - the matching rule. A bare shared-token floor was implemented first and then MEASURED against the real fleet: a 140-item backlog, 254 branches, 534 open pull requests. It surfaced 61 candidates for one task, because in a fleet whose work shares one house vocabulary two words in common is met by almost everything, and a set nobody can read trains the acknowledgement into a reflex - worse than no scan. So a candidate must clear BOTH a shared-token floor (default 2, FM_SPAWN_OVERLAP_MIN_TOKENS) AND cover at least half (default 50, FM_SPAWN_OVERLAP_MIN_PERCENT) of the SMALLER of the two token vocabularies. That cut the same case to 12 with the genuine duplicate ranked first. Denominating by the smaller vocabulary rather than by the subject is also deliberate: a branch name carries four or five words against a subject and body carrying dozens, so subject-only denomination would put branches - the earliest evidence somebody already started - permanently out of reach. Both halves have their own test with a verified negative control. Candidates are ORDERED strongest evidence first and never bounded; the sort column is dropped immediately after sorting.

ACCEPTED IN THE PRIOR REVIEW ROUND, all six findings, five auto-fix and one the user decided:

  • silent-pr-listing-truncation (USER DECISION, options 1 AND 2 together): fm_pr_open_request_titles parses gh-axi's 'count: N of M total' header and FAILS the call whenever the total exceeds the window, so a truncated listing renders overlap=unavailable and never 'no open requests'; an absent or unparseable header with a full window also fails, since truncation cannot then be ruled out. The default FM_SPAWN_OVERLAP_PR_LIMIT was raised from 100 to 600 so the strict rule is not permanently unavailable on this repo, which currently reports 534 open pull requests. Verified against the real forge: 530 rows returned, rc=0, so the strict rule does not fire in practice.
  • stopword-stemming-order: stopword membership is checked BOTH before and after de-pluralization, so 'this'->'thi' and 'fixes'->'fixe' cannot escape the filter and count as shared signal.
  • unreadable-config-degrades-to-advisory: absent stays advisory, present-but-unreadable refuses.
  • branch-self-exclusion-overmatch: the task's-own-branch exclusion requires a path-component boundary, so fm/re-verify-green stays visible to task verify-green.
  • pr-title-tab-unsanitized: forge-supplied titles have tabs stripped like every other source.
  • unguarded-source-failure-truncates-sweep: the backlog awk and the per-meta grep are guarded so a source-read failure emits an unavailable row instead of aborting the subshell and silently truncating the sweep - the last uncovered path in the empty-set law.

OTHER ACCEPTED DECISIONS: the subject is the task id plus the first line of the brief's task statement. Sources are the backlog's open items, tasks holding live runtime metadata, local and remote branches, and open PR titles; a persistent secondmate is a home rather than a work item and is excluded. Results are recorded in state/.meta as overlap=, overlap_refs= and overlap_ack=, so 'nothing was compared' and 'compared, found nothing' stay distinguishable. The pull request listing goes to a temporary file rather than a command substitution because FM_PR_LIST_ERROR - the whole difference between unavailable and none - would not survive the subshell. config/spawn-overlap is inherited by secondmate homes. --overlap-ack is refused on --secondmate spawns.

WHY THIS RUN IS A RESTART: the previous run 01KZF6PK6BYFXK2CN2X7DTN4E9 died mid-fix-round when its review agent exited during an environment freeze, not on any finding. Its fix commit b183ab8 was preserved and recovered through the supported guarded custody recovery, so this branch carries both commits and no work was discarded or re-done.

EVIDENCE: the replay test was confirmed red twice by stashing the change and green with it. All 16 cases in tests/fm-spawn-overlap.test.sh pass, bin/fm-lint.sh is clean, and bin/fm-doc-audience-check.sh is ok. fm-spawn-batch, fm-spawn-dispatch-profile, fm-spawn-worktree-settle, fm-trace-context-spawn, fm-pr-merge, fm-pr-check-security, fm-teardown, fm-gotmp, fm-crew-state, fm-fleet-snapshot-view, fm-bearings-snapshot, fm-brief, fm-backend and fm-documentation-audiences all pass. One PRE-EXISTING failure in tests/fm-secondmate-harness.test.sh is unrelated and was confirmed to fail identically on the base commit: it is the worktree-tangle guard firing because the task worktree sits on a feature branch.

The rebase step was already decided for this branch: SKIP. The fork trunk is the deliberate base and the 46 commits ahead of upstream are the fork landing queue, never bundled into contribution history.

What Changed

  • bin/fm-spawn.sh now runs a duplicate-work overlap scan as the last gate before dispatch: candidate overlaps drawn from open backlog items, live task metadata, local and remote branches, and open PR titles are surfaced ordered strongest-evidence-first and recorded in state/<id>.meta (overlap=, overlap_refs=, overlap_ack=). A new config/spawn-overlap knob selects the posture — advisory (default, prints and records but never refuses), enforce (dispatch requires --overlap-ack naming every surfaced ref), or off — while an unknown value or a present-but-unreadable config file refuses.
  • bin/fm-pr-lib.sh gains the shared fm_pr_open_request_titles helper backing the scan's PR source, hardened across the review rounds to fail loud rather than open: it parses gh-axi's count: N of M total header, self-sizes the PR window, and fails truncated or short listings, so an unreachable forge or a guarded source-read failure yields overlap=unavailable — never overlap=none.
  • Candidate matching requires both a shared-token floor (FM_SPAWN_OVERLAP_MIN_TOKENS, default 2) and coverage of at least half of the smaller token vocabulary (FM_SPAWN_OVERLAP_MIN_PERCENT, default 50), with new coverage in tests/fm-spawn-overlap.test.sh (18 cases, all passing) replaying the recorded duplicate-dispatch incident red-before/green-after. Note the branch deliberately sits on the fork trunk, so its delta against origin/main also carries the 46-commit fork landing queue — the rebase step was skipped by prior decision, not oversight.

Risk Assessment

✅ Low: The fix commit implements all four accepted findings exactly as instructed - the account-for-the-whole-total invariant with the old comparison removed, a one-shot self-sizing window bounded by a documented and validated ceiling knob, brief-read gaps surfaced as unavailable instead of silently degrading, and the corrected reader comment - each behind tests that assert the guard through the executable interface (including call-count proofs of exactly-one re-list), with docs fully consistent and a read-only live-forge check confirming the complete-listing header shape the pass path depends on; the only residual note is a latent set -e hazard that cannot fire at any current call site.

Testing

Ran the full targeted suite tests/fm-spawn-overlap.test.sh (18/18 green at the target commit), proved the incident-replay test is red-capable by reverting the three implementation files to the feature's parent commit (the spawn dispatched the duplicate silently, exactly the recorded incident), and captured end-user CLI transcripts demonstrating the advisory candidate table, the enforce refusal-then-acknowledge flow with overlap_ack recorded in the spawn record, and the forge-outage overlap=unavailable path; also verified --overlap-ack is refused on secondmate spawns and spawn-overlap is inherited config. No UI surface is involved — the CLI transcript is the real end-user surface. All checks passed and the worktree was restored clean.

Evidence: Overlap suite green at target (18/18)
ok - replaying the recorded incident surfaces the duplicate task, branch, and pull request
ok - an unrelated task surfaces no overlap and records the empty set as checked
ok - an unreachable forge yields overlap=unavailable and never overlap=none
ok - a pull request listing smaller than the forge's open total yields overlap=unavailable and never overlap=none
ok - a brief that exists but cannot be read yields overlap=unavailable and never overlap=none
ok - an absent forge CLI yields overlap=unavailable and never overlap=none
ok - enforce mode refuses an unacknowledged live overlap and records the acknowledgement that clears it
ok - enforce mode refuses an incomplete overlap set, because an absent set is not a pass
ok - the default posture is loud and never a refusal
ok - off skips the scan and records that nothing was compared
ok - a misspelled overlap config value refuses instead of reading as absent
ok - a present but unreadable overlap config refuses instead of reading as absent
ok - a task under way with no backlog row is surfaced, and a second mate is not
ok - a branch that merely ends with the task id stays visible to the scan
ok - a long subject still reaches short branch names and no longer matches coincidences
ok - candidates are listed strongest evidence first, not in the order the sources were read
ok - the shared forge reader separates 'no open requests' from 'no answer' and from 'a listing short of the open total'
ok - the forge reader self-sizes its window once, bounded by the ceiling, and fails a listing still short of the total
# all fm-spawn-overlap tests passed
Evidence: Replay test red on pre-feature code (spawn dispatches the duplicate silently)

not ok - the replay did not report a candidate overlap (missing: 'overlap=candidates') --- output --- warning: .../brief.md records no delivery contract line ... spawned merge-path-verifies-no-ci-green harness=pi kind=ship mode=no-mistakes yolo=off window=firstmate:fm-merge-path-verifies-no-ci-green worktree=.../replay/wt

not ok - the replay did not report a candidate overlap (missing: 'overlap=candidates')
--- output ---
warning: /tmp/fm-spawn-overlap.1jpVRq/replay/home/data/merge-path-verifies-no-ci-green/brief.md records no delivery contract line (scaffolded before ship briefs recorded one); launching on the explicit --mode no-mistakes - confirm its definition of done matches
spawned merge-path-verifies-no-ci-green harness=pi kind=ship mode=no-mistakes yolo=off window=firstmate:fm-merge-path-verifies-no-ci-green worktree=/tmp/fm-spawn-overlap.1jpVRq/replay/wt
Evidence: CLI transcript: advisory mode surfaces the recorded incident's task, branch, and PR

$ fm-spawn.sh merge-path-verifies-no-ci-green <project> --mode no-mistakes --yolo off overlap=candidates overlap[3]{kind,ref,title,shared_tokens}: task,merge-refuses-unverified-green,"refuse merges without verified green checks (repo: alpha) (kind: ship) (since 2026-08-05)",green|merge|verifie branch,fm/merge-refuses-unverified-green,"",green|merge pr,1614,"fix(bin): refuse merges without verified green checks",green|merge These are CANDIDATES, not a verdict: read each one and decide whether it is the same work. ... spawned merge-path-verifies-no-ci-green ... [exit status: 0] $ grep overlap state/merge-path-verifies-no-ci-green.meta overlap=candidates overlap_refs=task:merge-refuses-unverified-green,branch:fm/merge-refuses-unverified-green,pr:1614

$ fm-spawn.sh merge-path-verifies-no-ci-green <project> --mode no-mistakes --yolo off
warning: /tmp/fm-overlap-demo.pIkFKF/demo-advisory/home/data/merge-path-verifies-no-ci-green/brief.md records no delivery contract line (scaffolded before ship briefs recorded one); launching on the explicit --mode no-mistakes - confirm its definition of done matches
overlap=candidates
overlap[3]{kind,ref,title,shared_tokens}:
  task,merge-refuses-unverified-green,"refuse merges without verified green checks (repo: alpha) (kind: ship) (since 2026-08-05)",green|merge|verifie
  branch,fm/merge-refuses-unverified-green,"",green|merge
  pr,1614,"fix(bin): refuse merges without verified green checks",green|merge
These are CANDIDATES, not a verdict: read each one and decide whether it is the same work. An 'unavailable' row means a source could not be read, so the set is incomplete - it is never a pass.
spawned merge-path-verifies-no-ci-green harness=pi kind=ship mode=no-mistakes yolo=off window=firstmate:fm-merge-path-verifies-no-ci-green worktree=/tmp/fm-overlap-demo.pIkFKF/demo-advisory/wt
[exit status: 0]

$ grep -E 'overlap' state/merge-path-verifies-no-ci-green.meta
worktree=/tmp/fm-overlap-demo.pIkFKF/demo-advisory/wt
project=/tmp/fm-overlap-demo.pIkFKF/demo-advisory/project
overlap=candidates
overlap_refs=task:merge-refuses-unverified-green,branch:fm/merge-refuses-unverified-green,pr:1614
Evidence: CLI transcript: enforce mode refuses, then dispatches with --overlap-ack recorded

error: merge-path-verifies-no-ci-green would dispatch against work already open, and --overlap-ack does not name: task:merge-refuses-unverified-green branch:fm/merge-refuses-unverified-green pr:1614 Read each candidate above ... re-run with --overlap-ack 'task:merge-refuses-unverified-green,branch:fm/merge-refuses-unverified-green,pr:1614'. Code lists candidates; it never decides equivalence. [exit status: 1] $ fm-spawn.sh ... --overlap-ack 'task:...,branch:...,pr:1614' spawned merge-path-verifies-no-ci-green ... [exit status: 0] meta: overlap_ack=task:merge-refuses-unverified-green,branch:fm/merge-refuses-unverified-green,pr:1614

$ cat config/spawn-overlap
enforce

$ fm-spawn.sh merge-path-verifies-no-ci-green <project> --mode no-mistakes --yolo off
warning: /tmp/fm-overlap-demo.YC0EeA/demo-enforce/home/data/merge-path-verifies-no-ci-green/brief.md records no delivery contract line (scaffolded before ship briefs recorded one); launching on the explicit --mode no-mistakes - confirm its definition of done matches
overlap=candidates
overlap[3]{kind,ref,title,shared_tokens}:
  task,merge-refuses-unverified-green,"refuse merges without verified green checks (repo: alpha) (kind: ship) (since 2026-08-05)",green|merge|verifie
  branch,fm/merge-refuses-unverified-green,"",green|merge
  pr,1614,"fix(bin): refuse merges without verified green checks",green|merge
These are CANDIDATES, not a verdict: read each one and decide whether it is the same work. An 'unavailable' row means a source could not be read, so the set is incomplete - it is never a pass.
error: merge-path-verifies-no-ci-green would dispatch against work already open, and --overlap-ack does not name:
  task:merge-refuses-unverified-green
  branch:fm/merge-refuses-unverified-green
  pr:1614
Read each candidate above, decide whether it is the same work, and either route this task onto the existing work or re-run with --overlap-ack 'task:merge-refuses-unverified-green,branch:fm/merge-refuses-unverified-green,pr:1614'. Code lists candidates; it never decides equivalence.
[exit status: 1]

$ fm-spawn.sh merge-path-verifies-no-ci-green <project> --mode no-mistakes --yolo off --overlap-ack 'task:merge-refuses-unverified-green,branch:fm/merge-refuses-unverified-green,pr:1614'
warning: /tmp/fm-overlap-demo.YC0EeA/demo-enforce/home/data/merge-path-verifies-no-ci-green/brief.md records no delivery contract line (scaffolded before ship briefs recorded one); launching on the explicit --mode no-mistakes - confirm its definition of done matches
overlap=candidates
overlap[3]{kind,ref,title,shared_tokens}:
  task,merge-refuses-unverified-green,"refuse merges without verified green checks (repo: alpha) (kind: ship) (since 2026-08-05)",green|merge|verifie
  branch,fm/merge-refuses-unverified-green,"",green|merge
  pr,1614,"fix(bin): refuse merges without verified green checks",green|merge
These are CANDIDATES, not a verdict: read each one and decide whether it is the same work. An 'unavailable' row means a source could not be read, so the set is incomplete - it is never a pass.
spawned merge-path-verifies-no-ci-green harness=pi kind=ship mode=no-mistakes yolo=off window=firstmate:fm-merge-path-verifies-no-ci-green worktree=/tmp/fm-overlap-demo.YC0EeA/demo-enforce/wt
[exit status: 0]

$ grep -E 'overlap' state/merge-path-verifies-no-ci-green.meta
worktree=/tmp/fm-overlap-demo.YC0EeA/demo-enforce/wt
project=/tmp/fm-overlap-demo.YC0EeA/demo-enforce/project
overlap=candidates
overlap_refs=task:merge-refuses-unverified-green,branch:fm/merge-refuses-unverified-green,pr:1614
overlap_ack=task:merge-refuses-unverified-green,branch:fm/merge-refuses-unverified-green,pr:1614
Evidence: CLI transcript: forge outage yields overlap=unavailable, never none

overlap=unavailable overlap[3]{kind,ref,title,shared_tokens}: unavailable,pr,"error: could not reach github.com: dial tcp: lookup github.com: no such host", task,merge-refuses-unverified-green,... meta: overlap=unavailable / overlap_refs=unavailable:pr,task:...,branch:...

$ fm-spawn.sh merge-path-verifies-no-ci-green <project> --mode no-mistakes --yolo off   # forge unreachable
warning: /tmp/fm-overlap-demo.zVbC3W/demo-outage/home/data/merge-path-verifies-no-ci-green/brief.md records no delivery contract line (scaffolded before ship briefs recorded one); launching on the explicit --mode no-mistakes - confirm its definition of done matches
overlap=unavailable
overlap[3]{kind,ref,title,shared_tokens}:
  unavailable,pr,"error: could not reach github.com: dial tcp: lookup github.com: no such host",
  task,merge-refuses-unverified-green,"refuse merges without verified green checks (repo: alpha) (kind: ship) (since 2026-08-05)",green|merge|verifie
  branch,fm/merge-refuses-unverified-green,"",green|merge
These are CANDIDATES, not a verdict: read each one and decide whether it is the same work. An 'unavailable' row means a source could not be read, so the set is incomplete - it is never a pass.
spawned merge-path-verifies-no-ci-green harness=pi kind=ship mode=no-mistakes yolo=off window=firstmate:fm-merge-path-verifies-no-ci-green worktree=/tmp/fm-overlap-demo.zVbC3W/demo-outage/wt
[exit status: 0]

$ grep -E 'overlap' state/merge-path-verifies-no-ci-green.meta
worktree=/tmp/fm-overlap-demo.zVbC3W/demo-outage/wt
project=/tmp/fm-overlap-demo.zVbC3W/demo-outage/project
overlap=unavailable
overlap_refs=unavailable:pr,task:merge-refuses-unverified-green,branch:fm/merge-refuses-unverified-green
Evidence: CLI transcript: secondmate --overlap-ack refusal and spawn-overlap inheritance
$ fm-spawn.sh some-task --secondmate --overlap-ack task:x
error: --overlap-ack applies only to ship and scout spawns; a secondmate is a persistent home rather than a work item, so no work overlaps it
[exit status: 1]

$ . bin/fm-config-inherit-lib.sh; echo "$FM_INHERITABLE_CONFIG"
crew-dispatch.json models.json crew-harness backlog-backend backend herdr-presentation-spaces startup-memory-budget trace-context spawn-overlap

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

⏭️ **Rebase** - skipped

Push main to origin, or rebase your branch onto origin/main, before gating.

⚠️ **Review** - 1 info
  • ⚠️ bin/fm-spawn.sh:1459 - The default FM_SPAWN_OVERLAP_PR_LIMIT=600 window is about to be exceeded by the live repo: a read-only check during this review shows the forge now reports 551 open pull requests, versus the 534 the intent measured when the default was chosen (~17/day growth). Within days, every ship/scout spawn's PR source will report overlap=unavailable (and enforce mode will require --overlap-ack unavailable:pr on every spawn) until the knob is raised. The intent raised the default to 600 specifically so the strict truncation rule is 'not permanently unavailable on this repo' - that property is expiring. Consider a larger default or documenting the expected operational response.
  • ℹ️ bin/fm-pr-lib.sh:518 - fm_pr_open_request_titles fails on truncation only when the forge-reported total exceeds the window (total > limit); the header's own listed-count N in 'count: N of M total' is never compared against M, so a listing where gh-axi returns fewer rows than the reported total for any reason other than the requested limit (M <= limit) passes as the complete open set. The function's contract comment at line 479 promises 'a listing truncated below the forge's reported open total' returns non-zero, and the test fixture itself encodes the gap (count: 2 of 523 with limit 600 asserts rc=0). Under currently verified gh-axi semantics (rows = min(limit, total)) the two rules coincide, and the implemented rule matches the accepted user decision verbatim - so tightening it (fail when header N < M) is the author's call and would require updating the fixture.
  • ℹ️ bin/fm-spawn.sh:1500 - The overlap subject extraction suppresses awk read errors (2>/dev/null) and masks the exit status behind the cut pipeline, so a brief that passes the -f existence check at line 1219 but cannot actually be read - or that lost its '# Task' heading - silently shrinks the subject to the bare task id, and the scan can then report overlap=none computed from id tokens alone. This is the only read-failure path in the scan that degrades silently rather than surfacing unavailable or refusing, which cuts against the change's own 'a present-but-unreadable file refuses' doctrine. Practical exposure is low (existence is checked, and neighboring brief reads share the same masking pattern), so this is a consistency question for the author rather than a defect.
  • ℹ️ bin/fm-pr-lib.sh:471 - The header comment on fm_pr_open_request_titles says fm-research-scan.sh's --landing prover and fm-spawn's overlap scan 'both ask it through one place', but fm-research-scan.sh:558 still runs its own inline 'gh-axi pr list --state all' reader (a genuinely different query that this open-only helper cannot serve). The comment overstates the helper's blast radius and could mislead a maintainer into believing changes here cover research-scan. Reword the comment to say research-scan asks a different (all-state, delivery) question with its own reader.

🔧 Fix: self-size PR window, fail short listings, surface brief gaps
1 info still open:

  • ℹ️ bin/fm-pr-lib.sh:521 - The new listed=$(printf &#39;%s\n&#39; &#34;$rows&#34; | grep -c .) assignment exits 1 whenever the listing is empty (grep -c reports 0 with status 1), which is the pass case for a repo with no open PRs. Every current call site is safe - fm-spawn's overlap_candidates invokes fm_pr_open_request_titles inside an elif condition where set -e is suspended for the whole function body, and the test suite runs under set -u only - but a future caller invoking the function under plain set -e outside a condition context would abort on the empty-open-set pass path. This fails loud rather than open, so the empty-set law is not violated; noting it because the prior fix round hardened exactly this hazard class elsewhere in the scan. A trailing || : guard on the assignment would remove the latency if ever desired.
✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-spawn-overlap.test.sh at target commit a1babfb — all 18 cases pass
  • git checkout ed376cf -- bin/fm-spawn.sh bin/fm-pr-lib.sh bin/fm-config-inherit-lib.sh &amp;&amp; bash tests/fm-spawn-overlap.test.sh — replay test red on pre-feature code (spawn dispatches the duplicate silently), then restored and confirmed clean
  • Manual CLI run of fm-spawn.sh on the recorded-incident fixture in advisory mode — candidate table surfaces the duplicate task, branch, and PR 1614; spawn proceeds; meta records overlap=candidates and overlap_refs
  • Manual CLI run in enforce mode — refusal names all three refs and the exact --overlap-ack value; re-run with it dispatches and records overlap_ack= in state/<id>.meta
  • Manual CLI run with unreachable forge — overlap=unavailable with the outage named, never overlap=none, persisted in the spawn record
  • fm-spawn.sh some-task --secondmate --overlap-ack task:x — refused with the documented reason
  • Sourced bin/fm-config-inherit-lib.sh — FM_INHERITABLE_CONFIG includes spawn-overlap
🔧 **Document** - 1 issue found → auto-fixed ✅
  • ℹ️ bin/fm-spawn.sh:1296 - The measurement comment in bin/fm-spawn.sh's overlap_match block says the matching rule was tuned against '95 open pull requests', while the author's intent narrative for the same measurement says the fleet reported 534 open pull requests (and FM_SPAWN_OVERLAP_PR_LIMIT was raised to 600 for that reason). Both numbers may be true in different frames - the measurement may have run under the pre-fix 100-row listing window - but I could not verify which describes the actual measured pool, so the empirical claim was left as written. Worth a one-word correction by the author if 95 is drift.

🔧 Fix: fix overlap comment drift: measurement window and ratio denominator
✅ Re-checked - no issues remain.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@sbracewell64
sbracewell64 force-pushed the fm/cfvc-02-duplicate-work-intake branch from dbd4cfe to d622db9 Compare August 9, 2026 00:12
fm-spawn already refused a duplicate LAUNCH - the same task id twice - and
nothing compared a new task's SUBJECT against work already open. That gap was
paid for once already: one fix, two tasks, two branches, two workers, and an
open pull request that already carried the change, with nothing detecting it.

Every ship and scout spawn now emits the candidate overlap set before any
endpoint exists: open backlog tasks, tasks holding live runtime metadata, local
and remote branches, and open pull request titles. Code emits candidates and
never decides equivalence; whether two prose descriptions are the same work
stays firstmate's judgment, made from a surfaced set instead of from memory.

A candidate must clear a shared-token floor AND cover half of the smaller of
the two vocabularies. Both halves are load-bearing: measured against a real
140-item backlog, 254 branches, and 95 open pull requests, the floor alone
surfaced 61 candidates for one task, while denominating by the subject alone
would put short branch names permanently out of reach. Candidates are ordered
strongest evidence first, never bounded, so nothing is hidden from the reader
or the gate.

The empty-set law binds: a source that cannot be read yields
overlap=unavailable, never overlap=none. The result is recorded in the task's
own metadata, so "nothing was compared" and "compared, found nothing" stay
distinguishable afterwards.

Enforcement follows the additive-inert pattern. config/spawn-overlap absent or
advisory prints and records without refusing; enforce additionally requires
--overlap-ack to name every surfaced ref; off skips the scan and says so. Any
other value refuses, because a safety knob that cannot be read must never be
treated as an absent one.

The pull request source goes through one new shared reader in fm-pr-lib.sh
rather than a second forge reader with its own failure semantics.

Tests replay the recorded incident and are red against the previous build by
construction, with negative controls for the unrelated task, the unreachable
forge, the absent forge CLI, the coincidental match, and the ordering.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant