flower
/
All briefs
in progress draft note flower
epic · Let's figure out we can turn your "waiting on your a...

Operator-initiated non-resolving "Discuss/Clarify" decision affordance

Dispatch

canonical · plan

Spec

markdown

hand-off · dispatch

Dispatch

Auto-dispatch

when it reaches planned

Design-loop

design pass before build

kind

No dispatch requests yet — dispatch above to generate a copy-paste packet.

provenance · append-only

Trace

live
or paste a screenshot uploading…
  1. link added 1h ago
    agent · flower-orchestrator
  2. link added 10h ago
    agent · flower-orchestrator
  3. link added 13h ago
    agent · system:commit-trailer
  4. link added 13h ago
    agent · system:commit-trailer
  5. participant joined 13h ago
    system · system:commit-trailer
  6. status change 17h ago
    agent · flower-orchestrator
  7. participant joined 17h ago
    system · flower-orchestrator
  8. plan proposed 18h ago

    **Design-loop COMPLETE → decomposed.** (flower-229-design, 2026-07-04) **Deliverable:** `docs/design/229-discuss-clarify-decision.md` — committed on branch `flower/229-discuss-clarify-design` @ `06c277c` (trailer `Brief: #229`). Full craft doc (mirrors the #226 shape): problem/goal, the 6 questions resolved, concept & boundaries, state model, thread mechanism, service+MCP surface, UI, bounds, liveness, reset/handoff, reuse audit, sequenced delivery, open questions, out of scope. **Process:** writer↔reviewer design-loop to consensus — grounded on the shipped decisions code via two code-map sub-agents (backend + UI); one adversarial review pass (verdict: **sound, light revision — not a redesign**) folded in: wake-message `reason` parameterization, blocking-lamp carve-out, daemon-reset activation note, notification/badge correction, two anchor fixes. **The 6 questions → resolved** (details in the spec above): (1) stay `open` + a denormalized `awaiting_turn` column, no new status; (2) **new `decision_messages` table** — refine's "reuse `decision_answers`" lean OVERTURNED with reasons (no author column, breaks the one-row resolve-once invariant, churns the frozen resolve path); (3) thread+composer in `x-decisions.detail` (flyout/deep-link) lifting the briefs Trace timeline, quiet "Discuss·N" card trigger, turn-aware lamp (blocking stays red); (4) soft round cap, no hard cap, Solo stays the chat bus; (5) reuse `decision_wake` + new `decision_discuss` MCP verb + broadcast (not wake) to the operator; (6) thread carries via role-addressing + **widen `redeliverTo()`/`orphaned()`** to close the one real reset-stranding gap. **Decomposed into 3 dependency-wired child PRs (each ≤~300 lines):** - **#258 PR-1 — Discussion spine** (data+service+MCP+event+pull). **Deps: none → dispatchable NOW.** - **#259 PR-2 — Operator Discuss UI** (thread+composer+turn lamp+card trigger). Deps: #258. - **#260 PR-3 — Bounds, liveness redelivery & reset/handoff hardening.** Deps: #258 (parallel to #259). Graph: **#258 → {#259, #260}.** Critical path to "operator discusses, daemon replies in the room": #258 → #259; #260 hardens. **Activation:** orchestrator should schedule a daemon reload (`flower:daemon-schema-reload`) after #258 merges so standing daemons pick up the reply loop (operator-facing #259 needs no reset). **#229 disposition:** design complete → **`planned`**, now the **parent** of the 3-PR mini-plan under epic #95. Net-new surface is small (1 table + 1 column + `discuss()` + `decision_discuss` + `DecisionDiscussed` + 1 thread partial + 1 sibling concern); resolve-once (#95 Q18), role-addressed handoff (#95 Q20), and the frozen answer affordance are all preserved unchanged. Ready for the orchestrator to dispatch **#258**.

    agent · flower-229-design
  9. status change 18h ago
    agent · flower-229-design
  10. refinement 18h ago

    # Operator-initiated non-resolving "Discuss/Clarify" decision affordance **Design pass COMPLETE** (writer↔reviewer design-loop to consensus, flower-229-design 2026-07-04; one adversarial review folded in — verdict: sound, light revision, not a redesign). Full craft doc: **`docs/design/229-discuss-clarify-decision.md`** (committed on branch, trailer `Brief: #229`). This spec is the durable, self-contained synthesis. **Scope was DESIGN ONLY — no app code changed.** ## Operator intent (preserved) When answering a decision, sometimes none of the options fit AND the operator wants to *talk it through* rather than commit — a **"Let's talk about this"** that opens a discussion **before** resolution instead of settling the call. Distinct from the `text` write-in (#228, resolves one-shot) and from PR-6/#122 follow-up chains (daemon-initiated, *after* an answer, a new decision). This is **operator-initiated, pre-resolution, NON-resolving** — a short clarification loop *inside* one open call. The annunciator's job is still to get the call answered; a discussion is a brief detour toward an answer, not a chat channel (Solo remains the durable chat bus). ## The six open questions → resolved 1. **State model → stay `open`.** No new status value; `DecisionStatus` stays `{open, answered, acked, dismissed}`. Add a denormalized `decisions.awaiting_turn ∈ {null, operator, daemon}` column — *whose move it is*, orthogonal to `status` exactly as `released_at` is (a turn marker, not a state). Drives the daemon pull + the operator lamp; written in the same tx as the message row. 2. **Thread mechanism → a new lightweight `decision_messages` table** (NOT generalize `decision_answers`). *refine's "reuse decision_answers" lean overturned, with reasons:* it has no author-kind column (needs one anyway), `recordAnswer()` does `firstOrCreate([])` = one row per decision (breaks resolve-once), and every answer consumer (`latestAnswer`/`answerLines`/denorm `decisions.answer`/ PR-6 lineage/backfill) would need a "not-a-discussion" filter — pushing churn onto the frozen resolve path. New table: `{decision_id, author_ref, author_kind(operator|daemon), body, timestamps}`, linear (no self-FK). Blast radius on the answer path: **zero**. 3. **UI → thread + composer in `x-decisions.detail`** (the #216 flyout + `/decisions/{decision}` deep-link), lifting the briefs Trace timeline (`<ol before:*>` rail + `addNote` composer). The **glance card** gets only a quiet **"Discuss · N"** trigger in the actions row (same `mono-label` tier as View more/Brief me). Turn-aware lamp overlay over `awaiting_turn` reusing existing tokens (operator turn → amber "your turn"; daemon turn → calmer `--live` "awaiting <role>"), **but a blocking call keeps `--danger` + 🛑 regardless of turn**. Hard constraints honored: `AnswersDecisions` + `affordance.blade.php` stay **unchanged** (new sibling concern `DiscussesDecisions`, like `BriefsDecisionsWithAi`); loading is `wire:target`-scoped (single-Livewire-component hazard). 4. **Bounds → soft round cap, no hard cap.** `config('flower.decisions.discussion.soft_round_cap', 6)` + a nudge banner ("resolve, or take it to Solo"); the real answer affordance is always one tap away; copy frames "clarify," not "chat"; a daemon-charter clause tells the assignee to reply concisely, drive to resolution, push open-ended threads to Solo. 5. **Liveness → reuse `decision_wake`** (#179) for the operator→daemon nudge (payload `reason` parameterized `discuss` vs `answered` so the daemon is told to reply, not ack); the daemon replies via a **new `decision_discuss` MCP verb**; operator notification is the existing broadcast (`DecisionDiscussed`) — no "wake a human." Dormant assignee → the thread waits, re-woken on the daemon's next check-in. 6. **Reset/handoff → carries durably** (thread + `awaiting_turn` live on the decision; role-addressed `assigned_to`). **One real gap closed:** `redeliverTo()` today re-wakes only released+unacked calls, so a mid-discussion (`open`, not released) call would strand behind the target-scoped-wake reset hazard (`decision_wake` not in `resetLifecycleKinds`, `reset_pending` never cleared on close). Fix: widen `redeliverTo()`/`orphaned()`/`HealthService.orphaned_decisions` to the `open ∧ awaiting_turn=daemon ∧ assigned_to=ref` set — reuses the exact #124 mechanism, no new primitive. ## Delivery / activation note The daemon-*reply* half goes live for a standing daemon only after it **resets onto the new code/charter** (existing `flower:daemon-schema-reload`/`daemon_request_reset` path) — the orchestrator schedules a reload after PR-1. Until then a pre-feature daemon that pulls a discuss call `decision_ack`s it → guarded no-op (no crash, call waits). The operator-facing half (PR-2) needs no reset. ## Sequenced PR plan (three child briefs of #229; each ≤~300 lines, `php artisan test` green) - **PR-1 — Discussion spine.** `decision_messages` table + `decisions.awaiting_turn` column; `DecisionMessage` model + relations + `scopeAwaitingDiscussionReplyFor`; `DecisionService::discuss()` (non-resolving; guard `open`; set `awaiting_turn`; operator turn → `wakeAssignee`); `answer/withdraw` null `awaiting_turn`; parameterize `enqueueDecisionWake` with `reason`; `DecisionDiscussed` event; MCP `decision_discuss`; union `awaitingDiscussionReplyFor` into `pullFor`/`recall_decisions` + `awaiting_turn`/bounded `messages[]` in the payload + `recall_decisions` Description branch. **Deps: none.** - **PR-2 — Operator Discuss UI.** `DiscussesDecisions` concern (`wire:target`-scoped); `x-decisions.thread` partial + composer in `x-decisions.detail`; soft-cap nudge banner; "Discuss · N" card trigger opening the existing flyout; turn-aware lamp overlay; `DecisionDiscussed` listener + `wire:poll` fallback. Review gate: `affordance.blade.php` + `AnswersDecisions.php` show no diff. **Deps: PR-1.** - **PR-3 — Bounds, liveness redelivery & handoff hardening.** `config` soft-cap; daemon-charter carve-out (pull+reply loop, concise, drive-to-resolution, Solo for open-ended); widen `redeliverTo()`/`orphaned()`/`HealthService` to mid-discussion calls; activation/reload note. **Deps: PR-1** (parallel to PR-2). **Dependency graph: PR-1 → {PR-2, PR-3}.** Critical path to "operator discusses, daemon replies in the room": **PR-1 → PR-2**; PR-3 hardens the corners. ## Reuse audit (net-new surface is small) 1 table + 1 column + 1 service method (`discuss`) + 1 MCP verb (`decision_discuss`) + 1 event (`DecisionDiscussed`) + 1 thread partial + 1 sibling Livewire concern. Everything else reuses: `decision_wake`/`redeliverTo`/`orphaned` (#179/#124), `DecisionChannels`/`decisionSummary` broadcast, the #216 flyout/deep-link + `x-decisions.detail`, the briefs Trace timeline + `addNote` composer, the bloom lamp palette + `AttentionSignal`, `pullFor`/`recall_decisions`. Resolve-once (#95 Q18), role-addressed handoff (#95 Q20), and the frozen answer affordance are all preserved unchanged. ## Relationships Parent **#95** (decisions epic). Builds on PR-6/#122 (follow-ups), #179 (decision_wake), #124 (orphaned-handoff redeliver), #216 (`/decisions` feed redesign). Complementary to #228 (text write-in). **Disposition:** design complete → decomposed into three `planned` child PRs (PR-1 dispatchable now). #229 is now the **parent** of that mini-plan under #95.

    agent · flower-229-design
  11. spec snapshot 18h ago

    # Operator-initiated non-resolving "Discuss/Clarify" decision affordance ## Problem / operator intent (2026-07-04) When answering a decision, sometimes none of the options fit AND the operator wants to *talk it through* rather than commit — a **"Let's talk about this"** response that opens a discussion stage instead of resolving the call. Free-form write-in (#228) lets the operator type a custom **answer** (still one-shot, resolving); this is different — a **NON-resolving, back-and-forth clarification** before the decision is settled. ## How it differs from what already exists - **Follow-up chains (PR-6/#122):** daemon-initiated, *after* an answer (`parent_answer_id` → threaded). This is **operator-initiated, *before* resolution**. - **`text` write-in (#228 / PR-3):** *resolves* one-shot. This **continues** — the decision stays open. ## Strawman (direction, to be refined) A **"Discuss / Clarify"** affordance on any pending decision card: the operator posts a message; the decision stays `open`/awaiting (**non-resolving**); the assigned daemon is woken (`decision_wake` — already works) and replies into a thread (reuse the `decision_answers` + threading plumbing from PR-6, generalized to operator-initiated turns); the thread accumulates until the operator picks a real answer to resolve. Mostly composes existing infra — not from scratch. ## Open design questions (→ a light design-loop) 1. **State model:** stay `open` with discussion events, or add an explicit `discussing` sub-state? (Lean: stay open — discussion is just non-resolving events; avoid a new terminal state.) 2. **Thread mechanism:** generalize `decision_answers` / `parent_answer_id` to carry operator-initiated non-resolving turns, or add a lightweight `decision_messages` seam? Reuse if clean. 3. **UI:** where does the thread live — inline on the card, the #216 flyout, or the `/decisions/{decision}` deep-link page — and how does it read alongside the lamp states? 4. **Bounds:** keep it for *clarification*, NOT open-ended chat (Solo is the durable chat bus). Cap rounds / nudge toward resolution? 5. **Liveness:** the daemon must be live to reply (fine now — decision_wake works); define behavior when the assignee is dormant/away (thread waits, like any pending decision). 6. **Reset/handoff:** a role-addressed decision mid-discussion inherits to the successor — confirm the thread carries over. ## Recommendation A light **design-loop** (writer↔reviewer) to resolve the 6 questions + produce a small PR plan, then build. Reuses PR-6 threading + `decision_wake`, so likely a modest build. ## Relationships - **Parent #95** (decisions epic); builds on PR-6/#122 (follow-up chains) + the #216 `/decisions` feed redesign. ## Provenance Operator ask 2026-07-04 ("Re: #170"); strawman + pushback by flower-refine (see #170 trail). Operator confirmed creating this brief 2026-07-04. **Disposition:** `refining` — design-loop candidate; open questions above must resolve before `planned`.

    system · flower-229-design
  12. participant joined 18h ago
    system · flower-229-design
  13. status change 23h ago
    agent · flower-refine
  14. parent set 23h ago

    Grouped under epic #95.

    agent · flower-refine
  15. plan proposed 23h ago

    # Operator-initiated non-resolving "Discuss/Clarify" decision affordance ## Problem / operator intent (2026-07-04) When answering a decision, sometimes none of the options fit AND the operator wants to *talk it through* rather than commit — a **"Let's talk about this"** response that opens a discussion stage instead of resolving the call. Free-form write-in (#228) lets the operator type a custom **answer** (still one-shot, resolving); this is different — a **NON-resolving, back-and-forth clarification** before the decision is settled. ## How it differs from what already exists - **Follow-up chains (PR-6/#122):** daemon-initiated, *after* an answer (`parent_answer_id` → threaded). This is **operator-initiated, *before* resolution**. - **`text` write-in (#228 / PR-3):** *resolves* one-shot. This **continues** — the decision stays open. ## Strawman (direction, to be refined) A **"Discuss / Clarify"** affordance on any pending decision card: the operator posts a message; the decision stays `open`/awaiting (**non-resolving**); the assigned daemon is woken (`decision_wake` — already works) and replies into a thread (reuse the `decision_answers` + threading plumbing from PR-6, generalized to operator-initiated turns); the thread accumulates until the operator picks a real answer to resolve. Mostly composes existing infra — not from scratch. ## Open design questions (→ a light design-loop) 1. **State model:** stay `open` with discussion events, or add an explicit `discussing` sub-state? (Lean: stay open — discussion is just non-resolving events; avoid a new terminal state.) 2. **Thread mechanism:** generalize `decision_answers` / `parent_answer_id` to carry operator-initiated non-resolving turns, or add a lightweight `decision_messages` seam? Reuse if clean. 3. **UI:** where does the thread live — inline on the card, the #216 flyout, or the `/decisions/{decision}` deep-link page — and how does it read alongside the lamp states? 4. **Bounds:** keep it for *clarification*, NOT open-ended chat (Solo is the durable chat bus). Cap rounds / nudge toward resolution? 5. **Liveness:** the daemon must be live to reply (fine now — decision_wake works); define behavior when the assignee is dormant/away (thread waits, like any pending decision). 6. **Reset/handoff:** a role-addressed decision mid-discussion inherits to the successor — confirm the thread carries over. ## Recommendation A light **design-loop** (writer↔reviewer) to resolve the 6 questions + produce a small PR plan, then build. Reuses PR-6 threading + `decision_wake`, so likely a modest build. ## Relationships - **Parent #95** (decisions epic); builds on PR-6/#122 (follow-up chains) + the #216 `/decisions` feed redesign. ## Provenance Operator ask 2026-07-04 ("Re: #170"); strawman + pushback by flower-refine (see #170 trail). Operator confirmed creating this brief 2026-07-04. **Disposition:** `refining` — design-loop candidate; open questions above must resolve before `planned`.

    agent · flower-refine
  16. note added 23h ago

    Operator ask (2026-07-04, "Re: #170"): a "Let's talk about this" response to a decision that opens a discussion stage instead of resolving it. Distinct from the shipped follow-up chains (PR-6/#122, daemon-initiated post-answer) — this is operator-initiated, pre-resolution, NON-resolving. Needs a light design pass. See #170 event trail for the strawman + pushback.

    agent · flower-refine
  17. participant joined 23h ago
    system · flower-refine

epic · dependencies

Relationships

depends on

No dependencies — dispatchable once planned.

agents · waves

Participants

  • flower-refine participant · active
  • flower-229-design participant · active
  • flower-orchestrator participant · active
  • system:commit-trailer participant · active

trace · graph

Links

  • Scratchpad #399 execution
  • Scratchpad #398 execution
  • Commit #4064 execution
  • Commit #4065 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.