BriefComposer sends the predecessor report twice when an enrich role name equals handoff.from #690
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
rule-proposal
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#690
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
brief_composer.rb compose builds { node:, instructions:, brief:, handoff: handoff_json }.merge(enriched_facts). handoff_json carries the immediate predecessor's content. An enrich entry that is a role name resolves through latest_handoff_content(from_agent_type:), which returns the same row on first entry. Two nodes always enter from the role they enrich: seam-designer enriches change-auditor (poodr-implementation.yaml:26, poodr-ticket-implementation.yaml:33) and test-writer enriches seam-designer (poodr-implementation.yaml:43, poodr-ticket-implementation.yaml:50). Role reports measure median 1,587 chars, max 8,497, so the duplicate can cost up to 8.5k of a 10,000-unit hook cap. The enrich entry is still needed on re-entry (for example test-reviewer fail routes back to test-writer, where handoff.from is test-reviewer).
Reproduce
n/a: design audit finding, not a runtime defect.
Approach
In BriefComposer#compose, when handoff.from equals a name in the node's enrich list, send handoff as { id, from } only. The enrich key keeps the content because charters read the report by role name. On re-entry (test-reviewer fail to test-writer) handoff.from is not an enriched name, so its content stays. Do this in the composer so HookPayload measures the reduced size.
Required work:
Acceptance criteria
Owner sketch
plugins/os-sdlc/lib/os_sdlc/pipeline/brief_composer.rb (compose, handoff_json). Tests: plugins/os-sdlc/tests/brief_composer_test.rb or the existing test file that covers BriefComposer.
Evidence
#676 audit note from the seam-designer and test-writer rows plus the handoff-path check of brief_composer.rb:17-36.
Origin
• Trigger: #676 enrich-fact audit, run in session 9434289b
• Improvised this session: none
• Chain: oversized or duplicated brief facts <- enrich lists chosen per node without a per-role byte audit <- DESIGN (map enrich model)
• Root candidate: this ticket
• Where: the map/charter/composer paths named in the draft
• Parent: #676
• Session: 9434289b-c48f-4a8a-8add-0d82068e2c3f
• Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/9434289b-c48f-4a8a-8add-0d82068e2c3f.jsonl
Skeptic verdict: CREATE (os-backlog:ticket-skeptic, session 9434289b, citations verified).
Second-opinion review (Codex, session 9434289b): RISK MEDIUM. seam-designer.md:11 and test-writer.md:18 tell the role to read the fact by name. A marker value leaves the key present but empty. Prefer the other option: keep the enrich key and drop the duplicate from the handoff field, or dedupe by content in HookPayload only when the cap would trip.
Shape correction before implementation: keep the content under the enrich key (charters read it by name at seam-designer.md:11 and test-writer.md:18) and reduce the handoff field to { id, from } when handoff.from equals an enriched role name. Do not replace the enrich value with a marker.
Decision (session 9434289b): drop handoff.content, keep the enrich key. Rule: when handoff.from equals a name in the node's enrich list, the composer sends handoff as { id, from } only. Reason: charters read the report by role name; handoff.from is still needed to say which node routed here; on re-entry (test-reviewer fail to test-writer) handoff.from is not an enriched name so its content, the objections, stays. Do it in BriefComposer#compose so the HookPayload cap sees the reduced size.
Work started on branch ticket-690 via /os-sdlc:implement, session 9434289b.
Resolution
Done: BriefComposer#handoff_json returns { id, from } when handoff.from is a name in the node enrich list; the enrich key carries the content. Handoffs from non-enriched roles and nil handoffs are unchanged. Three tests in tests/pipeline/brief_composer_test.rb.
Evidence: os-sdlc implementation 26, 8 dispatches all pass, 0 retries, 514s; suite gate green (40,504 ms); branch ticket-690, 2 files.
Follow-ups: none