#534 D: Fact owners on the models #540

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

Context

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

Tasks

  • Add Dispatch#step_summaries method that returns step_results.to_h { |r| [r.step, r.summary] }
  • Add StepResult#summary method that returns { "verdict" => exit_status.zero? ? Verdict::PASS : Verdict::FAIL, "output" => output }
  • Move lint failures logic to StepResult.lint_failures_for(implementation) class method; three-table join, ordered by id
  • Add Handoff.latest(implementation, from:) class method for joined_dataset where from_agent_type, desc id, first
  • Create new LintOffenseLine class (was BriefFacts::LintFailureRow); move unchanged behavior; add #lines method for parsed offenses or raw 200-char line
  • Create new TestDiff class; initialize with (project, root:); add #to_s method that returns git_diff output or empty string when no test paths
  • Add Implementation#latest_dispatch method as Dispatch.latest_completed_for(id) wrapper

Pseudo-Ruby:

class Dispatch
  def step_summaries = step_results.to_h { |r| [r.step, r.summary] }
end

class StepResult
  def summary = { "verdict" => exit_status.zero? ? Verdict::PASS : Verdict::FAIL, "output" => output }
  def self.lint_failures_for(implementation)
end

class Handoff
  def self.latest(implementation, from:)
end

class LintOffenseLine
  def initialize(step_result_row)
  def lines
end

class TestDiff
  def initialize(project, root:)
  def to_s = git_diff(DiffedTestPaths.new(root, project).paths)
end

Acceptance criteria

  • each moved method keeps the existing BriefFacts test expectations, re-homed
  • all new model methods are tested

Blocking edges

Blocked by: #535 and #539

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 - [ ] Add Dispatch#step_summaries method that returns step_results.to_h { |r| [r.step, r.summary] } - [ ] Add StepResult#summary method that returns { "verdict" => exit_status.zero? ? Verdict::PASS : Verdict::FAIL, "output" => output } - [ ] Move lint failures logic to StepResult.lint_failures_for(implementation) class method; three-table join, ordered by id - [ ] Add Handoff.latest(implementation, from:) class method for joined_dataset where from_agent_type, desc id, first - [ ] Create new LintOffenseLine class (was BriefFacts::LintFailureRow); move unchanged behavior; add #lines method for parsed offenses or raw 200-char line - [ ] Create new TestDiff class; initialize with (project, root:); add #to_s method that returns git_diff output or empty string when no test paths - [ ] Add Implementation#latest_dispatch method as Dispatch.latest_completed_for(id) wrapper Pseudo-Ruby: ```ruby class Dispatch def step_summaries = step_results.to_h { |r| [r.step, r.summary] } end class StepResult def summary = { "verdict" => exit_status.zero? ? Verdict::PASS : Verdict::FAIL, "output" => output } def self.lint_failures_for(implementation) end class Handoff def self.latest(implementation, from:) end class LintOffenseLine def initialize(step_result_row) def lines end class TestDiff def initialize(project, root:) def to_s = git_diff(DiffedTestPaths.new(root, project).paths) end ``` ## Acceptance criteria - [ ] each moved method keeps the existing BriefFacts test expectations, re-homed - [ ] all new model methods are tested ## Blocking edges Blocked by: #535 and #539 ## 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-540 (session d233a2f7-977a-419d-99ea-011b6e1e66f8).

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

Pipeline implementation 2 (dispatches 16-26) returned implementation_complete after one increment: StepResult#summary only (commit on ticket-540). behavior-verifier passed with six of seven tasks untouched, a wrong completion verdict. Not closing. Opening a new implementation on the same branch for the remaining tasks.

Pipeline implementation 2 (dispatches 16-26) returned implementation_complete after one increment: StepResult#summary only (commit on ticket-540). behavior-verifier passed with six of seven tasks untouched, a wrong completion verdict. Not closing. Opening a new implementation on the same branch for the remaining tasks.
Author
Owner

Resolution

Done: Fact owners live on the models: StepResult#summary, Dispatch#step_summaries, StepResult.lint_failures_for(implementation), Handoff.latest(implementation, from:), LintOffenseLine (replaces BriefFacts::LintFailureRow), TestDiff(project, root:)#to_s, Implementation#latest_dispatch. BriefFacts asks them and no longer builds joins or duplicates the verdict rule. Every moved behavior keeps its BriefFacts expectations and every new model method has a direct test.

Evidence: Branch ticket-540, seven per-increment commits acc1862, cf32bb2, 3e52065, 68f1036, 120095e, 789bbaa, f3e9232 plus history row 10a055a, merged to main 2026-09-09. Suite 1109 runs green. Pipeline: implementation 2 (dispatches 16-26) declared complete after one task, rejected; implementation 3 (dispatches 27-86) landed the rest and escalated on dispatch_cap:60 at the last suite-check with the suite green; the final five-line increment (Implementation#latest_dispatch wrapper, BriefFacts#gate_results asks the aggregate) was reviewed by the orchestrator instead of contract-auditor and behavior-verifier. Two flake reruns (autocorrect prepass whitespace race, getcwd hook race). Escalation capture: see step 1 result.

Follow-ups: #541 and #542 next in the #534 chain; escalation cause captured per step 1 (ticket number or #540 comment)

## Resolution **Done:** Fact owners live on the models: StepResult#summary, Dispatch#step_summaries, StepResult.lint_failures_for(implementation), Handoff.latest(implementation, from:), LintOffenseLine (replaces BriefFacts::LintFailureRow), TestDiff(project, root:)#to_s, Implementation#latest_dispatch. BriefFacts asks them and no longer builds joins or duplicates the verdict rule. Every moved behavior keeps its BriefFacts expectations and every new model method has a direct test. **Evidence:** Branch ticket-540, seven per-increment commits acc1862, cf32bb2, 3e52065, 68f1036, 120095e, 789bbaa, f3e9232 plus history row 10a055a, merged to main 2026-09-09. Suite 1109 runs green. Pipeline: implementation 2 (dispatches 16-26) declared complete after one task, rejected; implementation 3 (dispatches 27-86) landed the rest and escalated on dispatch_cap:60 at the last suite-check with the suite green; the final five-line increment (Implementation#latest_dispatch wrapper, BriefFacts#gate_results asks the aggregate) was reviewed by the orchestrator instead of contract-auditor and behavior-verifier. Two flake reruns (autocorrect prepass whitespace race, getcwd hook race). Escalation capture: see step 1 result. **Follow-ups:** #541 and #542 next in the #534 chain; escalation cause captured per step 1 (ticket number or #540 comment)
jared closed this issue 2026-09-09 17:25:48 +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#540
No description provided.