flower
/
All briefs
complete draft note flower

Live semantic search while typing new briefs in web UI to see if any _

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.

#105 done fresh flower · flower/219-briefs-live-search
agent: claude
You are being dispatched from flower Brief #219: Live semantic search while typing new briefs in web UI to see if any _

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

Target:
- project: flower (/Users/mikeferrara/Documents/code/flower)
- branch: flower/219-briefs-live-search
- worktree: not specified
- kind: fresh

Current brief spec:
## Goal
While the operator types a new brief in the /briefs capture box, run live semantic search against **active/open** briefs and surface likely matches inline ("there's already one like this!"), with actions to jump / continue / link / merge instead of unknowingly creating a duplicate. Motivating case: filing feedback/review requests that likely already exist.

## Operator decision (Q53, 2026-07-04) — offer ALL FOUR per-match actions (not passive-only):
1. **Open / jump** to the existing brief.
2. **Continue there** — append the typed note to that brief instead of creating a new one.
3. **Create new, then auto-link** it as a follow-up / child of the match.
4. **Offer merge** into the match after create.

## Approach (backends already exist — thin new caller + UI)
- **Search:** debounced (~300ms, min ~8 chars) hybrid semantic search on the capture textarea via the existing `recall_search` / `recall_briefs(query=…)` path (keyword+vector). Scope to **active/open** briefs only (idea/refining/planned/dispatched/in_progress/blocked/in_review) — exclude complete/cancelled/abandoned — per the operator's "active/open" framing. Same-project scope by default.
- **Surface:** a small "possible duplicates / related" panel under the capture box with the top 3–5 matches (title + status pill + similarity score + snippet). Non-blocking — the operator can ignore it and just create.
- **Per-match actions (all four):**
  - *Open* → navigate to `/briefs/{id}` (or open in a new tab).
  - *Continue there* → append the typed text to that brief as a `note_added`/`comment` event (`brief_append`) and go to it, instead of creating a new brief.
  - *Create + follow-up* → create the new brief, then link it as a child/follow-up of the match (`brief_set_parent`, or the follow-up-chain relation).
  - *Merge* → create the new brief, then `brief_merge(survivor=match, sources=[new])` to consolidate (offer merge direction).
- **Guard:** min-length + debounce so it doesn't fire per keystroke; cheap (reuses existing hybrid search); cancel stale in-flight queries (latest wins).

## Edge cases
- No matches / below score threshold → panel hidden (no noise).
- Empty/very short input → no search.
- Matches refresh as typing continues (latest query wins; cancel stale requests).
- "Continue there" on a brief that shouldn't be edited → degrade to Open.
- Operator ignores the panel → normal create path unaffected (non-blocking).

## Acceptance
- Typing in the /briefs capture box surfaces active/open semantic matches within ~300ms of a pause: top 3–5, each with title/status/score/snippet.
- Each match offers Open, Continue-there, Create+follow-up, and Merge — each mapping to the existing tool (`brief_append` / `brief_set_parent` / `brief_merge`).
- No matches or short input → no panel; the panel never blocks the create flow.

## Touch points / prior art
/briefs capture component (Livewire) + `recall_search` / `recall_briefs(query)` backend; `brief_append`, `brief_set_parent`, `brief_merge` for the actions; bloom `components/ui/*` for the panel. Relates to planned **#197** (search-UI: /briefs full-text search — could ship on the same surface) and epic **#87** (UI feedback).

_Status → planned. Refine stops here; orchestrator owns dispatch._

Recent/key trace events:
[1] participant_joined operator:mike: (no body)
[2] note_added operator:mike: Live semantic search while typing new briefs in web UI to see if any _active/open_ briefs are a good fit/match/related? If so - options to file the new brief as a follow up or whatever else might prove helpful in that scenario. 

Right now my use case for this was that I wanted to file some feedback/request for a review on various things that I'm pretty sure I already have and felt like it would be helpful if that was made clear _while typing out_ a new brief - "hey, look, there's one just like this already!" :D
[3] status_change operator:mike: (no body)
[4] participant_joined flower-refine: (no body)
[5] refinement flower-refine: **Refinement (flower-refine, 2026-07-04)**

**Ask:** as the operator types a new brief in the /briefs capture box, run live semantic search against **active/open** briefs and surface likely matches inline ("there's already one like this!"), with actions to file the note as a follow-up / continue-there / merge instead of duplicating. Motivating case: filing feedback/review requests that probably already exist.

**Grounding — the backends already exist; this is mostly a thin new caller + UI:**
- Hybrid semantic search: `recall_search` (keyword+vector) and `recall_briefs(query=…)` already power MCP + web search. A debounced as-you-type query is a thin new front-end caller, not new search infra.
- Consolidation actions already exist: `brief_merge(survivor,[sources])` for dup consolidation; follow-up/relate via `brief_set_parent` (epic/child) or decision follow-up chains. So "file as follow-up / merge" maps to existing tools.
- Overlaps/relates to planned **#197** (search-UI: /briefs full-text search) and epic **#87** (UI feedback) — could ship on that same surface.

**Proposed direction:**
- Debounced (~300ms, min ~8 chars) semantic search on the capture textarea → a small "possible duplicates / related" panel under the box with the top 3–5 active/open matches (title + status + score + snippet).
- Match scope default: **active/open** only (idea/refining/planned/dispatched/in_progress/blocked/in_review) — not complete/cancelled — per the operator's "active/open" framing.
- Per-match actions TBD by the decision below.
- Cost note: cheap (reuses existing hybrid search); debounce + min-length guards keep it from firing per keystroke.

**One decision opened (match actions). Holding at `refining`.**
[6] agent_question flower-refine: When similar active/open briefs surface while you're typing a new one, which per-match actions should we offer? (pick all that apply)
[7] operator_answer operator:mike: {"choices":["Open / jump to the existing brief","Continue there — append my note to that brief instead of creating new","Create new, then auto-link it as a follow-up / child of the match","Offer merge into the match after create"]}
[8] spec_snapshot flower-refine: Live semantic search while typing new briefs in web UI to see if any _active/open_ briefs are a good fit/match/related? If so - options to file the new brief as a follow up or whatever else might prove helpful in that scenario. 

Right now my use case for this was that I wanted to file some feedback/request for a review on various things that I'm pretty sure I already have and felt like it would be helpful if that was made clear _while typing out_ a new brief - "hey, look, there's one just like this already!" :D
[9] refinement flower-refine: ## Goal
While the operator types a new brief in the /briefs capture box, run live semantic search against **active/open** briefs and surface likely matches inline ("there's already one like this!"), with actions to jump / continue / link / merge instead of unknowingly creating a duplicate. Motivating case: filing feedback/review requests that likely already exist.

## Operator decision (Q53, 2026-07-04) — offer ALL FOUR per-match actions (not passive-only):
1. **Open / jump** to the existing brief.
2. **Continue there** — append the typed note to that brief instead of creating a new one.
3. **Create new, then auto-link** it as a follow-up / child of the match.
4. **Offer merge** into the match after create.

## Approach (backends already exist — thin new caller + UI)
- **Search:** debounced (~300ms, min ~8 chars) hybrid semantic search on the capture textarea via the existing `recall_search` / `recall_briefs(query=…)` path (keyword+vector). Scope to **active/open** briefs only (idea/refining/planned/dispatched/in_progress/blocked/in_review) — exclude complete/cancelled/abandoned — per the operator's "active/open" framing. Same-project scope by default.
- **Surface:** a small "possible duplicates / related" panel under the capture box with the top 3–5 matches (title + status pill + similarity score + snippet). Non-blocking — the operator can ignore it and just create.
- **Per-match actions (all four):**
  - *Open* → navigate to `/briefs/{id}` (or open in a new tab).
  - *Continue there* → append the typed text to that brief as a `note_added`/`comment` event (`brief_append`) and go to it, instead of creating a new brief.
  - *Create + follow-up* → create the new brief, then link it as a child/follow-up of the match (`brief_set_parent`, or the follow-up-chain relation).
  - *Merge* → create the new brief, then `brief_merge(survivor=match, sources=[new])` to consolidate (offer merge direction).
- **Guard:** min-length + debounce so it doesn't fire per keystroke; cheap (reuses existing hybrid search); cancel stale in-flight queries (latest wins).

## Edge cases
- No matches / below score threshold → panel hidden (no noise).
- Empty/very short input → no search.
- Matches refresh as typing continues (latest query wins; cancel stale requests).
- "Continue there" on a brief that shouldn't be edited → degrade to Open.
- Operator ignores the panel → normal create path unaffected (non-blocking).

## Acceptance
- Typing in the /briefs capture box surfaces active/open semantic matches within ~300ms of a pause: top 3–5, each with title/status/score/snippet.
- Each match offers Open, Continue-there, Create+follow-up, and Merge — each mapping to the existing tool (`brief_append` / `brief_set_parent` / `brief_merge`).
- No matches or short input → no panel; the panel never blocks the create flow.

## Touch points / prior art
/briefs capture component (Livewire) + `recall_search` / `recall_briefs(query)` backend; `brief_append`, `brief_set_parent`, `brief_merge` for the actions; bloom `components/ui/*` for the panel. Relates to planned **#197** (search-UI: /briefs full-text search — could ship on the same surface) and epic **#87** (UI feedback).

_Status → planned. Refine stops here; orchestrator owns dispatch._
[10] status_change flower-refine: (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: #219` 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/219-briefs-live-search into master on MAIN — merge commit **d1f12b4** (worker commit 977804c). Clean 3-way merge, no cross-branch collision (distinct from the /decisions + /feedback work). Full suite green on MAIN (1098 passed, 2 skips, 6835 assertions); pint clean; npm build done (/briefs capture + duplicates panel). No Horizon reload (inline RecallService caller). Worker proc 1127 closed.

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

    Dispatch request #105 marked done.

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

    Implemented on branch `flower/219-briefs-live-search` (commit 977804c). Backends already existed — this was a thin caller + UI. **Files (3, +422/-5):** - `app/Livewire/Briefs/Index.php` — refactored `capture()` → `persistNoteBrief()` (creates the draft brief without clearing/redirecting so panel actions can link/merge first). Added `duplicateMatches()` (reuses `RecallService->search`, `sources=['brief']`, scoped to the primary capture project by default, filtered to the 7 active/open statuses, top 5) + `duplicateMatchRow()`, and the four action methods `continueThere` / `createAndFollowUp` / `createAndMerge` (+ `matchIsEditable` guard). `render()` passes `duplicateMatches`. - `resources/views/livewire/briefs/index.blade.php` — capture textarea now `wire:model.live.debounce.300ms`; new non-blocking bloom panel under the capture box (title + status pill + score + snippet) with all four per-match actions. - `tests/Feature/Briefs/IndexTest.php` — 6 new tests via a bound stub `SearchService`. **Approach / decisions:** - Guards: min 8 chars + 300ms client debounce so it never fires per keystroke; Livewire supersedes the prior in-flight request → latest wins; panel hidden on short/empty input or no matches. Normal create path untouched. - Actions map to existing tools per spec: Open → `/briefs/{id}`; Continue-there → `brief_append` (note_added) then open, **degrades to Open on a terminal/merged match**; Create+follow-up → `BriefRelationService::setParent(new, match)` (epic child); Merge → `BriefRelationService::merge(survivor=match, sources=[new])`. - Snippet prefers the brief's own `summary` (cleaner than raw chunk text); score shown as the raw hybrid rank (matches the existing search console convention). **Tests:** full suite green — 1083 passed / 3 pre-existing skips / 0 failures. `pint` clean on changed PHP. Blade compiles/renders (exercised via the panel test's `->html()`). Not merged — leaving the merge to the orchestrator on MAIN.

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

    Dispatch request #105 queued for flower.

    agent · flower-orchestrator
  9. status change 1d ago
    agent · flower-orchestrator
  10. participant joined 1d ago
    system · flower-orchestrator
  11. status change 1d ago
    agent · flower-refine
  12. refinement 1d ago

    ## Goal While the operator types a new brief in the /briefs capture box, run live semantic search against **active/open** briefs and surface likely matches inline ("there's already one like this!"), with actions to jump / continue / link / merge instead of unknowingly creating a duplicate. Motivating case: filing feedback/review requests that likely already exist. ## Operator decision (Q53, 2026-07-04) — offer ALL FOUR per-match actions (not passive-only): 1. **Open / jump** to the existing brief. 2. **Continue there** — append the typed note to that brief instead of creating a new one. 3. **Create new, then auto-link** it as a follow-up / child of the match. 4. **Offer merge** into the match after create. ## Approach (backends already exist — thin new caller + UI) - **Search:** debounced (~300ms, min ~8 chars) hybrid semantic search on the capture textarea via the existing `recall_search` / `recall_briefs(query=…)` path (keyword+vector). Scope to **active/open** briefs only (idea/refining/planned/dispatched/in_progress/blocked/in_review) — exclude complete/cancelled/abandoned — per the operator's "active/open" framing. Same-project scope by default. - **Surface:** a small "possible duplicates / related" panel under the capture box with the top 3–5 matches (title + status pill + similarity score + snippet). Non-blocking — the operator can ignore it and just create. - **Per-match actions (all four):** - *Open* → navigate to `/briefs/{id}` (or open in a new tab). - *Continue there* → append the typed text to that brief as a `note_added`/`comment` event (`brief_append`) and go to it, instead of creating a new brief. - *Create + follow-up* → create the new brief, then link it as a child/follow-up of the match (`brief_set_parent`, or the follow-up-chain relation). - *Merge* → create the new brief, then `brief_merge(survivor=match, sources=[new])` to consolidate (offer merge direction). - **Guard:** min-length + debounce so it doesn't fire per keystroke; cheap (reuses existing hybrid search); cancel stale in-flight queries (latest wins). ## Edge cases - No matches / below score threshold → panel hidden (no noise). - Empty/very short input → no search. - Matches refresh as typing continues (latest query wins; cancel stale requests). - "Continue there" on a brief that shouldn't be edited → degrade to Open. - Operator ignores the panel → normal create path unaffected (non-blocking). ## Acceptance - Typing in the /briefs capture box surfaces active/open semantic matches within ~300ms of a pause: top 3–5, each with title/status/score/snippet. - Each match offers Open, Continue-there, Create+follow-up, and Merge — each mapping to the existing tool (`brief_append` / `brief_set_parent` / `brief_merge`). - No matches or short input → no panel; the panel never blocks the create flow. ## Touch points / prior art /briefs capture component (Livewire) + `recall_search` / `recall_briefs(query)` backend; `brief_append`, `brief_set_parent`, `brief_merge` for the actions; bloom `components/ui/*` for the panel. Relates to planned **#197** (search-UI: /briefs full-text search — could ship on the same surface) and epic **#87** (UI feedback). _Status → planned. Refine stops here; orchestrator owns dispatch._

    agent · flower-refine
  13. spec snapshot 1d ago

    Live semantic search while typing new briefs in web UI to see if any _active/open_ briefs are a good fit/match/related? If so - options to file the new brief as a follow up or whatever else might prove helpful in that scenario. Right now my use case for this was that I wanted to file some feedback/request for a review on various things that I'm pretty sure I already have and felt like it would be helpful if that was made clear _while typing out_ a new brief - "hey, look, there's one just like this already!" :D

    system · flower-refine
  14. operator answer 1d ago

    {"choices":["Open / jump to the existing brief","Continue there — append my note to that brief instead of creating new","Create new, then auto-link it as a follow-up / child of the match","Offer merge into the match after create"]}

    operator · operator:mike
  15. agent question 1d ago

    When similar active/open briefs surface while you're typing a new one, which per-match actions should we offer? (pick all that apply)

    agent · flower-refine
  16. refinement 1d ago

    **Refinement (flower-refine, 2026-07-04)** **Ask:** as the operator types a new brief in the /briefs capture box, run live semantic search against **active/open** briefs and surface likely matches inline ("there's already one like this!"), with actions to file the note as a follow-up / continue-there / merge instead of duplicating. Motivating case: filing feedback/review requests that probably already exist. **Grounding — the backends already exist; this is mostly a thin new caller + UI:** - Hybrid semantic search: `recall_search` (keyword+vector) and `recall_briefs(query=…)` already power MCP + web search. A debounced as-you-type query is a thin new front-end caller, not new search infra. - Consolidation actions already exist: `brief_merge(survivor,[sources])` for dup consolidation; follow-up/relate via `brief_set_parent` (epic/child) or decision follow-up chains. So "file as follow-up / merge" maps to existing tools. - Overlaps/relates to planned **#197** (search-UI: /briefs full-text search) and epic **#87** (UI feedback) — could ship on that same surface. **Proposed direction:** - Debounced (~300ms, min ~8 chars) semantic search on the capture textarea → a small "possible duplicates / related" panel under the box with the top 3–5 active/open matches (title + status + score + snippet). - Match scope default: **active/open** only (idea/refining/planned/dispatched/in_progress/blocked/in_review) — not complete/cancelled — per the operator's "active/open" framing. - Per-match actions TBD by the decision below. - Cost note: cheap (reuses existing hybrid search); debounce + min-length guards keep it from firing per keystroke. **One decision opened (match actions). Holding at `refining`.**

    agent · flower-refine
  17. participant joined 1d ago
    system · flower-refine
  18. status change 1d ago
    agent · operator:mike
  19. note added 1d ago

    Live semantic search while typing new briefs in web UI to see if any _active/open_ briefs are a good fit/match/related? If so - options to file the new brief as a follow up or whatever else might prove helpful in that scenario. Right now my use case for this was that I wanted to file some feedback/request for a review on various things that I'm pretty sure I already have and felt like it would be helpful if that was made clear _while typing out_ a new brief - "hey, look, there's one just like this already!" :D

    operator · operator:mike
  20. participant joined 1d ago
    system · operator:mike

epic · dependencies

Relationships

epic parent

depends on

No dependencies — dispatchable once planned.

agents · waves

Participants

  • operator:mike participant · active
  • flower-refine participant · active
  • flower-orchestrator participant · active
  • flower-219-worker participant · active
  • system:commit-trailer participant · active

trace · graph

Links

  • Commit #4011 execution

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.