flower
/
All briefs
complete draft mcp flower
epic · Redesign /decisions feed to deliver decision context...

[#216 PR-2] /decisions Capture seam — enrich DecisionAutoLinkService to link sessions/commits/feedback/parent

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.

#102 done fresh flower · flower/221-decisions-capture-seam
agent: claude
You are being dispatched from flower Brief #221: [#216 PR-2] /decisions Capture seam — enrich DecisionAutoLinkService to link sessions/commits/feedback/parent

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

Target:
- project: flower (/Users/mikeferrara/Documents/code/flower)
- branch: flower/221-decisions-capture-seam
- 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-216-worker: (no body)
[2] note_added flower-216-worker: Child of Brief #216. Design doc: `docs/design/216-decisions-feed-redesign.md` §"Sequenced delivery" PR-2. Sequenced AFTER PR-1 (#220) — PR-2 populates the trace-summary + detail link list PR-1 renders.

## Scope (≈ ≤300 lines)
Today `DecisionAutoLinkService::syncDecision()` (`app/Services/Briefs/DecisionAutoLinkService.php:30`) links ONLY the subject brief as `reference` — so the "linked context" list is empty or one row. Extend it to derive and `link()` the originating context, reusing the existing idempotent `link(Decision, Model, BriefLinkRelation, auto=true)` (`:46`, `firstOrCreate` on `[linkable_type, linkable_id, relation]`) — no signature change needed:
- **Sessions/segments** — from the subject brief's participants (`brief_participants.session_id`).
- **Commits** — from the subject brief's events/links (the brief's own commit links).
- **Feedback** — from `brief.feedback_id` (the source feedback).
- **Parent/epic** — the brief's `parent_brief_id` brief.

Pick relations from the existing `BriefLinkRelation` vocab (Seed/PlanSource/Execution/Result/Reference) — keep the subject-brief `Reference` self-link as-is (PR-1's trace-summary dedupes it). Bound the fan-out (cap N per bucket, most-recent-first) so a hot brief doesn't link hundreds of rows.

Add a **backfill artisan command** to sync open decisions retroactively so existing calls populate.

## Constraints
- `link()` stays idempotent — re-running `syncDecision()` must not duplicate.
- Writes `decision_links` directly (no `DecisionService` dep — avoid the container cycle, as the class doc notes).
- Confirm the linked morph types match PR-1's trace-summary bucket mapping (Session/Segment/Commit/Feedback/parent-Brief) so buckets populate as designed (design doc Open question "Trace-summary buckets").

## Acceptance
A decision with a real subject brief shows populated trace-summary counts + a multi-row grouped linked-context list; backfill covers open decisions; no duplicate links on re-sync; `php artisan test` green; `pint` clean.
[3] parent_set flower-216-worker: Grouped under epic #216.
[4] dependency_added flower-216-worker: Now depends on #220 ([#216 PR-1] /decisions Display seam — promote provenance + subject + trace onto the card face).
[5] participant_joined flower-220-worker: (no body)
[6] unblocked flower-220-worker: Unblocked — #220 reached complete.
[7] participant_joined flower-orchestrator: (no body)
[8] 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: #221` 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/221-decisions-capture-seam into master on MAIN — merge commit **576c17e** (worker commit 89d80fa). Full suite green on MAIN (1088 passed, 2 pre-existing skips, 6744 assertions); pint clean. No Horizon reload: DecisionAutoLinkService has zero Jobs/Listeners references — it runs inline from DecisionService (MCP/web), so web + new sessions get new code immediately; standing daemon MCP servers self-heal on next reset. TODO (post-#222): run `flower:backfill-decision-links` on MAIN to enrich existing decisions so /decisions buckets populate on real data. Worker proc 1124 closed.

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

    Dispatch request #102 marked done.

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

    **PR-2 Capture seam landed (flower-221-worker, dispatch #102).** Branch `flower/221-decisions-capture-seam`, commit **89d80fa** (off master f54c7ac). NOT merged — orchestrator merges from MAIN. **What changed** - `app/Services/Briefs/DecisionAutoLinkService.php` (+116) — `syncDecision()` now, for a Brief-subject decision, also links the originating context off the subject brief, reusing the unchanged idempotent `link(Decision, Model, BriefLinkRelation, auto)` (firstOrCreate). No `DecisionService` dep; writes `decision_links` directly. - **Sessions** → subject brief's participant `session_id`s, as `Execution`. - **Commits** → the brief's own commit links (`brief_links` where `linkable_type=Commit`), as `Result`. - **Feedback** → `brief.feedback_id`, as `Seed`. - **Parent/epic** → `brief.parent_brief_id`, as `Reference` (distinct `linkable_id` from the subject self-link). - Bounded: `MAX_PER_BUCKET=5` sessions/commits, most-recent-first (participants by `last_seen_at` desc; commit links by `ts` desc). Feedback + parent are single-valued. - `app/Console/Commands/BackfillDecisionLinks.php` (new) — `flower:backfill-decision-links {project?} {--all} {--dry-run}`. Replays the additive/idempotent sync over existing decisions (open by default; every non-standalone decision with `--all`; project-scoped by id/slug/name/root_path). `--dry-run` reports `in_scope=N` without writing. Uses `eachById` chunking; summary line `processed/links_created/decisions_enriched`. - Tests: `tests/Feature/Decisions/DecisionAutoLinkServiceTest.php` (+enrichment: each bucket+relation, cap-most-recent-first, enriched idempotency; existing subject/idempotent/standalone stay green) and new `tests/Feature/Console/BackfillDecisionLinksCommandTest.php` (enrich / dry-run / --all / project-scope). **Morph-type ↔ bucket confirmation (design doc Open question):** no morph map in the app, so `linkable_type` is the full class name and `class_basename` yields `Session`/`Commit`/`Feedback`/`Brief` — exactly what PR-1's `components/decisions/trace-summary.blade.php` buckets (Session/Segment→sessions, Commit→commits, Feedback→feedback, parent-Brief-by-id→epic; subject-brief Reference self-link deduped by `id===subject_id`). Buckets populate as designed. **Verification:** full suite green (`~/bin/php artisan test` → 1088 passed, 2 pre-existing skips, 6744 assertions); `pint --test` clean on all four files. syncDecision fires unchanged from `DecisionService` ask/answer paths (lines 72/121), so live decisions enrich on next mutate; the backfill covers already-open ones.

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

    Dispatch request #102 queued for flower.

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

    Unblocked — #220 reached complete.

    system · flower-220-worker
  13. participant joined 1d ago
    system · flower-220-worker
  14. dependency added 1d ago

    Now depends on #220 ([#216 PR-1] /decisions Display seam — promote provenance + subject + trace onto the card face).

    agent · flower-216-worker
  15. parent set 1d ago

    Grouped under epic #216.

    agent · flower-216-worker
  16. note added 1d ago

    Child of Brief #216. Design doc: `docs/design/216-decisions-feed-redesign.md` §"Sequenced delivery" PR-2. Sequenced AFTER PR-1 (#220) — PR-2 populates the trace-summary + detail link list PR-1 renders. ## Scope (≈ ≤300 lines) Today `DecisionAutoLinkService::syncDecision()` (`app/Services/Briefs/DecisionAutoLinkService.php:30`) links ONLY the subject brief as `reference` — so the "linked context" list is empty or one row. Extend it to derive and `link()` the originating context, reusing the existing idempotent `link(Decision, Model, BriefLinkRelation, auto=true)` (`:46`, `firstOrCreate` on `[linkable_type, linkable_id, relation]`) — no signature change needed: - **Sessions/segments** — from the subject brief's participants (`brief_participants.session_id`). - **Commits** — from the subject brief's events/links (the brief's own commit links). - **Feedback** — from `brief.feedback_id` (the source feedback). - **Parent/epic** — the brief's `parent_brief_id` brief. Pick relations from the existing `BriefLinkRelation` vocab (Seed/PlanSource/Execution/Result/Reference) — keep the subject-brief `Reference` self-link as-is (PR-1's trace-summary dedupes it). Bound the fan-out (cap N per bucket, most-recent-first) so a hot brief doesn't link hundreds of rows. Add a **backfill artisan command** to sync open decisions retroactively so existing calls populate. ## Constraints - `link()` stays idempotent — re-running `syncDecision()` must not duplicate. - Writes `decision_links` directly (no `DecisionService` dep — avoid the container cycle, as the class doc notes). - Confirm the linked morph types match PR-1's trace-summary bucket mapping (Session/Segment/Commit/Feedback/parent-Brief) so buckets populate as designed (design doc Open question "Trace-summary buckets"). ## Acceptance A decision with a real subject brief shows populated trace-summary counts + a multi-row grouped linked-context list; backfill covers open decisions; no duplicate links on re-sync; `php artisan test` green; `pint` clean.

    agent · flower-216-worker
  17. participant joined 1d ago
    system · flower-216-worker

epic · dependencies

Relationships

depends on

agents · waves

Participants

  • flower-216-worker participant · active
  • flower-220-worker participant · active
  • flower-orchestrator participant · active
  • flower-221-worker participant · active
  • system:commit-trailer participant · active

trace · graph

Links

  • Commit #4014 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.