flower
/
All briefs
complete draft feedback flower
from feedback #110 · brief-autolink false-positives "target_branch_merged...

Fix brief-autolink false "target_branch_merged" + spurious result-links on zero-commit branches (fb #110)

canonical · plan

Spec

markdown

hand-off · dispatch

Dispatch

Auto-dispatch

when it reaches planned

Design-loop

design pass before build

This brief is complete — dispatch is closed.

#108 done fresh flower · flower/110-autolink-merge-guard
agent: claude
You are being dispatched from flower Brief #227: Fix brief-autolink false "target_branch_merged" + spurious result-links on zero-commit branches (fb #110)

Recall pointer:
- Use recall_brief with id 227 for the full folder if you need provenance.

Target:
- project: flower (/Users/mikeferrara/Documents/code/flower)
- branch: flower/110-autolink-merge-guard
- worktree: not specified
- kind: fresh

Current brief spec:
(no spec yet)

This is a direct request, not a fully-specced plan. If it's clear, resolve it. If you hit a blocking ambiguity, call brief_ask (or brief_append) with your questions and flip the brief to `refining` before proceeding — don't guess.

Recent/key trace events:
[1] participant_joined flower-orchestrator: (no body)
[2] note_added flower-orchestrator: ## Bug (autonomous / Funnel B — fb #110, self-reported by orchestrator, code-confirmed by flower-ops). Authoritative fix-spec: Solo scratchpad 1089.
A dispatched brief whose worktree branch was freshly created off master HEAD (the STANDARD dispatch flow here) is falsely flagged by brief-autolink BEFORE its first commit: (a) a `target_branch_merged` status_suggestion + Comment ("suggest marking complete") on in-flight work, and (b) 6 unrelated default-branch merge commits linked as the brief's `result`. Hit live on #223 (branch tip == master HEAD → reads as "merged").

## Root cause (ops-confirmed in code)
- **(a)** `app/Services/Briefs/BriefAutoLinkService.php::suggestCompleteIfMerged()` (L586–628) gates the suggestion SOLELY on `$this->git->mergeStatus($path, $branch, $default) === 'merged'` (L593–594). No check that the branch has ≥1 commit unique to it. A zero-commit branch's tip == default HEAD ⇒ mergeStatus reports 'merged' ⇒ false "suggest complete".
- **(b)** The spurious result-commit linking is a SEPARATE path (NOT in suggestCompleteIfMerged — grep this file's result/commit-linking; likely in the auto-linker/commit layer). It currently grabs recent DEFAULT-branch merges as the brief's result set.

## Fix (both facets share the same gate)
1. **(a)** In `suggestCompleteIfMerged`, additionally require the branch to have unique commits before suggesting complete — `git rev-list <default>..<branch>` non-empty (equivalently branch tip != default HEAD). Guard belongs at L594 alongside the merge check.
2. **(b)** In the result-commit linking path, only link commits in the branch's UNIQUE range (`git log <default>..<branch>`), NEVER the default branch's own merges. Same ≥1-unique-commit gate.
3. **Regression test(s):** a brief on a branch with tip == default HEAD (zero unique commits) gets NO complete-suggestion and NO result-commit links; once the branch has ≥1 unique commit that is actually merged, the suggestion + only-its-own-commit links fire correctly. Mock/stub the Git wrapper as the existing BriefAutoLinkService tests do.

## Out of scope (orchestrator handles separately on MAIN)
The 6 already-written spurious `result` BriefLink rows on brief #223 (d3d3700/9671c9a/d1f12b4/0afe7d3/576c17e/f54c7ac) are a data cleanup the orchestrator will do directly on MAIN — do NOT attempt a shared-MySQL data fix from the worktree.

## Acceptance
Zero-commit branch → no false suggest-complete + no result links; real merged branch with unique commits → correct behavior; regression test green; `php artisan test` green + `pint` clean. `Brief: #<this id>` trailer. Marking complete auto-closes fb #110.
[3] link_added flower-orchestrator: (no body)
[4] status_change flower-orchestrator: (no body)

Recommended linked context:
{
    "todos": [],
    "scratchpads": []
}

Execution notes:
- Treat the brief as the source of truth.
- Keep work scoped to this dispatch request.
- Use brief_append / brief_update_status when reporting material progress; as your final dispatched-worker step, call brief_dispatch_complete with dispatch_request_id (or brief_id) and actor_ref.
- Codex workers should verify mutating Flower tools with tool_search query `brief_append brief_dispatch_complete flower_feedback` (limit 20) when tool availability is in doubt; report raw SEE/LOAD vs NOT visible instead of silently using local fallbacks.
- Add a git commit trailer `Brief: #227` to every commit for this brief so flower can exact-link commits back to the brief.

provenance · append-only

Trace

live
or paste a screenshot uploading…
  1. link added 1d ago
    agent · system:commit-trailer
  2. participant joined 1d ago
    system · system:commit-trailer
  3. merged 1d ago

    Merged flower/110-autolink-merge-guard into master on MAIN — merge commit **fabc36f** (worker commit 58e4a07). Clean merge (4 files, +181). Full suite green on MAIN (1121 passed, 2 skips, 6934 assertions); pint clean; no npm build (service+test, no views). **Horizon gracefully reloaded** (BriefAutoLinkService runs in the IngestSession Horizon job → confirmed via grep app/Jobs; SIGTERM master 76466, auto-restart on fresh code) so the autolink guard is live. Worker proc 1130 closed. Auto-closed feedback #110. Fix: GitInspector::branchTipDiffersFromDefault + commitInBranchOnly gate both facets (suggest-complete + result-commit linking) on the branch tip diverging from default HEAD. Worker correctly flagged that the spec's literal `rev-list default..branch` phrasing is incompatible with mergeStatus==='merged' and implemented the tip-SHA comparison instead — good catch. Verified live: the #223 spurious-link scenario is now guarded, and the 6 bad #223 links were already cleaned on MAIN. Full dogfooding loop closed: orchestrator-reported bug #110 → ops triage/code-confirm → orchestrator dispatch → merged.

    agent · flower-orchestrator
  4. status change 1d ago
    agent · flower-227-worker
  5. dispatched 1d ago

    Dispatch request #108 marked done.

    agent · flower-227-worker
  6. note added 1d ago

    Fixed & committed (58e4a07, branch flower/110-autolink-merge-guard, `Brief: #227` trailer). Full suite green (1121 passed / 2 pre-existing skips), pint clean. ## Files - `app/Services/Discovery/GitInspector.php` — two thin fail-soft helpers mirroring `mergeStatus`/`branchContainsCommit`: - `branchTipDiffersFromDefault(dir, branch, default)` — true iff branch tip SHA != default HEAD SHA. - `commitInBranchOnly(dir, branch, sha, default)` — true iff sha is reachable from branch but NOT from default (the `default..branch` range). - `app/Services/Briefs/BriefAutoLinkService.php`: - **Facet (a)** `suggestCompleteIfMerged()` — added the `branchTipDiffersFromDefault` gate right after the `mergeStatus === 'merged'` check (L594 area). - **Facet (b)** result-commit linking (the `syncCommit` `branchContainsCommit` block, L105–110) — swapped `branchContainsCommit` → `commitInBranchOnly` so only unique-range commits link, never default-branch merges. (Facet (b) was NOT in `suggestCompleteIfMerged`; it's the `branch_commit`→`Result` relation path, confirmed via `BriefService::autoLinkRules`.) - Tests: `GitInspectorDivergenceTest` (3 new unit tests) + `BriefAutoLinkServiceTest::test_zero_commit_branch_...` (real-git regression covering both facets: zero-commit branch → 0 changes, no Result link, no suggestion). The pre-existing `test_commit_on_target_branch_links_result_and_merged_branch_suggests_complete...` already covers the merged-with-real-work case and stays green. ## Design note (important) The fix-spec's literal "`git rev-list default..branch` non-empty" is **incompatible** with the existing `mergeStatus === 'merged'` condition: by definition `merged` ⟺ tip is an ancestor of default ⟺ `rev-list default..branch` is empty. Verified empirically — a real merged branch and a zero-commit branch BOTH have `rev-list default..branch == 0`; the ONLY distinguisher is `tip == default HEAD`. So I implemented the spec's parenthetical (`branch tip != default HEAD`), which is the correct, compatible check and keeps the merged-branch suggestion working. ## Residual (out of scope, noted for awareness) Narrow timing gap: if the default branch advances (another brief merges) while a dispatch branch still has zero commits, its tip becomes an old default commit != new HEAD, so the tip-comparison gate would let a false suggestion through. This is advisory-only (no auto status change) and requires that specific window; the reporter's rev-list phrasing would trade this for breaking real merges (worse). The live reported case (tip == default HEAD, #223) is fully fixed. Closing the residual robustly needs first-parent ancestry logic, which is beyond this brief's scope.

    agent · flower-227-worker
  7. participant joined 1d ago
    system · flower-227-worker
  8. dispatched 1d ago

    Dispatch request #108 queued for flower.

    agent · flower-orchestrator
  9. status change 1d ago
    agent · flower-orchestrator
  10. status change 1d ago
    agent · flower-orchestrator
  11. link added 1d ago
    agent · flower-orchestrator
  12. note added 1d ago

    ## Bug (autonomous / Funnel B — fb #110, self-reported by orchestrator, code-confirmed by flower-ops). Authoritative fix-spec: Solo scratchpad 1089. A dispatched brief whose worktree branch was freshly created off master HEAD (the STANDARD dispatch flow here) is falsely flagged by brief-autolink BEFORE its first commit: (a) a `target_branch_merged` status_suggestion + Comment ("suggest marking complete") on in-flight work, and (b) 6 unrelated default-branch merge commits linked as the brief's `result`. Hit live on #223 (branch tip == master HEAD → reads as "merged"). ## Root cause (ops-confirmed in code) - **(a)** `app/Services/Briefs/BriefAutoLinkService.php::suggestCompleteIfMerged()` (L586–628) gates the suggestion SOLELY on `$this->git->mergeStatus($path, $branch, $default) === 'merged'` (L593–594). No check that the branch has ≥1 commit unique to it. A zero-commit branch's tip == default HEAD ⇒ mergeStatus reports 'merged' ⇒ false "suggest complete". - **(b)** The spurious result-commit linking is a SEPARATE path (NOT in suggestCompleteIfMerged — grep this file's result/commit-linking; likely in the auto-linker/commit layer). It currently grabs recent DEFAULT-branch merges as the brief's result set. ## Fix (both facets share the same gate) 1. **(a)** In `suggestCompleteIfMerged`, additionally require the branch to have unique commits before suggesting complete — `git rev-list <default>..<branch>` non-empty (equivalently branch tip != default HEAD). Guard belongs at L594 alongside the merge check. 2. **(b)** In the result-commit linking path, only link commits in the branch's UNIQUE range (`git log <default>..<branch>`), NEVER the default branch's own merges. Same ≥1-unique-commit gate. 3. **Regression test(s):** a brief on a branch with tip == default HEAD (zero unique commits) gets NO complete-suggestion and NO result-commit links; once the branch has ≥1 unique commit that is actually merged, the suggestion + only-its-own-commit links fire correctly. Mock/stub the Git wrapper as the existing BriefAutoLinkService tests do. ## Out of scope (orchestrator handles separately on MAIN) The 6 already-written spurious `result` BriefLink rows on brief #223 (d3d3700/9671c9a/d1f12b4/0afe7d3/576c17e/f54c7ac) are a data cleanup the orchestrator will do directly on MAIN — do NOT attempt a shared-MySQL data fix from the worktree. ## Acceptance Zero-commit branch → no false suggest-complete + no result links; real merged branch with unique commits → correct behavior; regression test green; `php artisan test` green + `pint` clean. `Brief: #<this id>` trailer. Marking complete auto-closes fb #110.

    agent · flower-orchestrator
  13. participant joined 1d ago
    system · flower-orchestrator

epic · dependencies

Relationships

epic parent

depends on

No dependencies — dispatchable once planned.

agents · waves

Participants

  • flower-orchestrator participant · active
  • flower-227-worker participant · active
  • system:commit-trailer participant · active

trace · graph

Links

  • Commit #4020 execution
  • Feedback #110 seed

scope

Projects

  • flower · primary

dogfood · read-only

Agent’s-eye view

The literal recall_brief payload an agent gets — same service path as the MCP tool.