#534 E: BriefFacts asks, never queries #541

Closed
opened 2026-09-08 20:15:25 +00:00 by jared · 2 comments
Owner

Context

Child of #534 (ADR-0169). Parent design points and comments on #534. Lands after #535.

Tasks

  • Refactor BriefFacts to have a public #fact(name) method that asks for facts by name
  • Define FACTS constant with %w[ticket_brief gate_results lint_offense_history test_changes]
  • Implement BriefFacts#fact(name) to return send(name) if in FACTS; return handoff_from(name) if in AgentType::ALL; raise UnknownFact otherwise
  • Implement private methods: ticket_brief, gate_results, lint_offense_history, test_changes, handoff_from
  • Keep HANDOFF_FACT_AGENT_TYPES alive in this child so the map still works (F deletes it)
  • Delete @tracker.fetch_body fallback
  • Update brief_composer.rb:40-41 so enrich key becomes the section heading

Pseudo-Ruby:

class BriefFacts
  MAX_GATE_OUTPUT_CHARS = 4000
  FACTS = %w[ticket_brief gate_results lint_offense_history test_changes].freeze

  def initialize(implementation, root:) = (@implementation, @root = implementation, root)

  def fact(name)
    return send(name)         if FACTS.include?(name)
    return handoff_from(name) if AgentType::ALL.include?(name)
    raise UnknownFact, name
  end

  private

  def ticket_brief = implementation.ticket_body
  def gate_results = implementation.latest_dispatch&.step_summaries&.transform_values { truncate_output(_1) } || {}
  def lint_offense_history = truncate(StepResult.lint_failures_for(implementation).flat_map { LintOffenseLine.new(_1).lines }.uniq.join("\n"))
  def test_changes = truncate(TestDiff.new(implementation.project(root), root: root).to_s).then { _1.empty? ? nil : _1 }
  def handoff_from(agent) = Handoff.latest(implementation, from: agent)&.content
  def truncate(text) = text.to_s[0, MAX_GATE_OUTPUT_CHARS]
end

Acceptance criteria

  • brief_facts tests run on the multi-target fixture
  • a subagent-start on a multi-target project produces a programmer brief with the target's test path

Blocking edges

Blocked by: #535 and #540

Origin

  • Trigger: Agent-initiated from ticket-skeptic gate (CREATE verdict)
  • Improvised this session: none
  • Chain: DESIGN ← ADR-0169
  • Root candidate: #534
  • Where: n/a
  • Session: 1a3b7fd0-0319-465b-8414-0ab70560de3d
  • Transcript: (from session context)
  • Judge: claude-sonnet-5 on 2026-09-08; user-decided reframe recorded in ADR-0169; former child G merged into B
## Context Child of #534 (ADR-0169). Parent design points and comments on #534. Lands after #535. ## Tasks - [ ] Refactor BriefFacts to have a public #fact(name) method that asks for facts by name - [ ] Define FACTS constant with %w[ticket_brief gate_results lint_offense_history test_changes] - [ ] Implement BriefFacts#fact(name) to return send(name) if in FACTS; return handoff_from(name) if in AgentType::ALL; raise UnknownFact otherwise - [ ] Implement private methods: ticket_brief, gate_results, lint_offense_history, test_changes, handoff_from - [ ] Keep HANDOFF_FACT_AGENT_TYPES alive in this child so the map still works (F deletes it) - [ ] Delete @tracker.fetch_body fallback - [ ] Update brief_composer.rb:40-41 so enrich key becomes the section heading Pseudo-Ruby: ```ruby class BriefFacts MAX_GATE_OUTPUT_CHARS = 4000 FACTS = %w[ticket_brief gate_results lint_offense_history test_changes].freeze def initialize(implementation, root:) = (@implementation, @root = implementation, root) def fact(name) return send(name) if FACTS.include?(name) return handoff_from(name) if AgentType::ALL.include?(name) raise UnknownFact, name end private def ticket_brief = implementation.ticket_body def gate_results = implementation.latest_dispatch&.step_summaries&.transform_values { truncate_output(_1) } || {} def lint_offense_history = truncate(StepResult.lint_failures_for(implementation).flat_map { LintOffenseLine.new(_1).lines }.uniq.join("\n")) def test_changes = truncate(TestDiff.new(implementation.project(root), root: root).to_s).then { _1.empty? ? nil : _1 } def handoff_from(agent) = Handoff.latest(implementation, from: agent)&.content def truncate(text) = text.to_s[0, MAX_GATE_OUTPUT_CHARS] end ``` ## Acceptance criteria - [ ] brief_facts tests run on the multi-target fixture - [ ] a subagent-start on a multi-target project produces a programmer brief with the target's test path ## Blocking edges Blocked by: #535 and #540 ## Origin - Trigger: Agent-initiated from ticket-skeptic gate (CREATE verdict) - Improvised this session: none - Chain: DESIGN ← ADR-0169 - Root candidate: #534 - Where: n/a - Session: 1a3b7fd0-0319-465b-8414-0ab70560de3d - Transcript: (from session context) - Judge: claude-sonnet-5 on 2026-09-08; user-decided reframe recorded in ADR-0169; former child G merged into B
Author
Owner

Work started via /os-sdlc:implement on branch ticket-541 (session d233a2f7-977a-419d-99ea-011b6e1e66f8).

Work started via /os-sdlc:implement on branch ticket-541 (session d233a2f7-977a-419d-99ea-011b6e1e66f8).
Author
Owner

Resolution

Done: BriefFacts asks, never queries: fact(name) returns the four direct facts (ticket_brief, gate_results, lint_offense_history, test_changes), resolves any AgentType::ALL name to that agent's latest handoff content, keeps the HANDOFF_FACT_AGENT_TYPES aliases alive for the map, and raises UnknownFact otherwise. The tracker fetch_body fallback is deleted: ticket_brief is the stored body or nil. test_changes diffs against the implementation-bound target's test path. The FACTS constant and the composer heading tasks were judged naming-only by the behavior-verifier: FACT_RESOLVERS already lists the four facts and brief_composer.rb already keys each enriched section by its enrich name.

Evidence: Pipeline /os-sdlc:implement 541, implementation 4, session d233a2f7-977a-419d-99ea-011b6e1e66f8, dispatches 87-109, ended implementation_complete. Commits 1a7189c, 40e5775 and history row 6ed104f on branch ticket-541, merged to main 2026-09-09. Suite 1113 runs green. New tests: brief_facts_multi_target_test.rb, brief_facts_handoff_from_agent_type_test.rb. Deviations: (1) one pre-existing test asserting the tracker fallback was deleted by hand because programmer-repair may not edit tests (#544 shape); (2) three test-reviewer rounds on the first increment, two for a harness call and a constant assertion; (3) the orchestrator committed each verified increment on the branch so reviewers saw test-only diffs.

Follow-ups: #542 next (handoff facts by convention, deletes HANDOFF_FACT_AGENT_TYPES); the now-unused tracker: keyword on BriefFacts#initialize is left for #542 to remove with the aliases

## Resolution **Done:** BriefFacts asks, never queries: fact(name) returns the four direct facts (ticket_brief, gate_results, lint_offense_history, test_changes), resolves any AgentType::ALL name to that agent's latest handoff content, keeps the HANDOFF_FACT_AGENT_TYPES aliases alive for the map, and raises UnknownFact otherwise. The tracker fetch_body fallback is deleted: ticket_brief is the stored body or nil. test_changes diffs against the implementation-bound target's test path. The FACTS constant and the composer heading tasks were judged naming-only by the behavior-verifier: FACT_RESOLVERS already lists the four facts and brief_composer.rb already keys each enriched section by its enrich name. **Evidence:** Pipeline /os-sdlc:implement 541, implementation 4, session d233a2f7-977a-419d-99ea-011b6e1e66f8, dispatches 87-109, ended implementation_complete. Commits 1a7189c, 40e5775 and history row 6ed104f on branch ticket-541, merged to main 2026-09-09. Suite 1113 runs green. New tests: brief_facts_multi_target_test.rb, brief_facts_handoff_from_agent_type_test.rb. Deviations: (1) one pre-existing test asserting the tracker fallback was deleted by hand because programmer-repair may not edit tests (#544 shape); (2) three test-reviewer rounds on the first increment, two for a harness call and a constant assertion; (3) the orchestrator committed each verified increment on the branch so reviewers saw test-only diffs. **Follow-ups:** #542 next (handoff facts by convention, deletes HANDOFF_FACT_AGENT_TYPES); the now-unused tracker: keyword on BriefFacts#initialize is left for #542 to remove with the aliases
jared closed this issue 2026-09-09 18:07:17 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#541
No description provided.