Callers stop asking Project for a default target; Facts#bound_target crashed every enriching hook on shaped projects (#795 regression) #803

Closed
opened 2026-09-28 17:04:33 +00:00 by jared · 4 comments
Owner

Problem

#795 (commit 150727e8) removed the flat default target from Project. Project#target(nil) now raises TargetSelectionError on a project whose yaml has named targets. Three callers still ask for the default target. The first, Brief::Facts#bound_target, runs in the SubagentStart hook for every pipeline node that enriches test_changes or code_changes. Claude Code swallows a failing hook, so the agent starts with no brief and no dispatch id.

Observed

Implementation 61 (#796) on the os-sdlc target. Two test-reviewer dispatches returned without a brief.

os-sdlc-subagent-start < start.json
os-sdlc-subagent-start: harness error: OsSdlc::CLI::TargetSelectionError: no target selected -- .sdlc/project.yaml defines named targets, so every run must name one (--target NAME). Available: os-backlog, cc-architect, os-sdlc, os, os-aidd-lint, os-adr

Backtrace: model/project.rb:32 Project#target ← query/brief/facts.rb:114 Facts#bound_target ← Facts#codebase ← Facts#changes ← Facts#test_changes.

Reproduce

printf '{"session_id":"<id>","agent_type":"os-sdlc:test-reviewer","cwd":"<worktree>","hook_event_name":"SubagentStart"}' | os-sdlc-subagent-start

Approach

Callers read the bound target from the object that knows it, never from Project with no name.

Required work:

  1. Brief::Facts#bound_target returns @implementation.target (the accessor command/gate/run.rb:55 already uses). — check: ! grep -n "project.target(nil)" plugins/os-sdlc/lib/os_sdlc/query/brief/facts.rb
  2. Project#status_line no longer calls target(nil): its one caller command/cli/project_config_command.rb:91 prints one line per target for a shaped project. — check: bin/os-sdlc project listing on this repo exits 0
  3. Project#resolved_lint_config takes the target name or reads it from its caller command/autocorrect_prepass/pass.rb:35. — check: ! grep -n "target(nil)" plugins/os-sdlc/lib/os_sdlc/model/project.rb
  4. The suite is green. — check: ruby plugins/os-sdlc/tests/all.rb

Rejected: restore a flat default target on Project. #795 removed it on purpose; the run's target belongs to the Implementation.

Acceptance criteria

  1. Given an implementation bound to a named target on a shaped project, when the test-reviewer SubagentStart hook runs, then it exits 0 and prints the brief.
  2. Given this repo's shaped yaml, when the project config listing runs, then it exits 0 and prints every target.
  3. Given the full suite, when it runs, then it passes.

Owner sketch

class OsSdlc::Brief::Facts
  def bound_target = @implementation.target
end

Non-goals

Item 1 is hand-fixed in this session on branch hotfix-795-bound-target; items 2 and 3 stay open on this ticket.

Origin

  • Trigger: os-sdlc-subagent-start for test-reviewer exit 3 during /os-sdlc:implement 796
  • Improvised this session: none
  • Chain: hook crash ← Facts#bound_target calls Project#target(nil) ← DESIGN #795 commit 150727e8 removed the flat default target
  • Root candidate: this ticket
  • Where: plugins/os-sdlc/lib/os_sdlc/query/brief/facts.rb
  • Session: 854e0e91-e925-44c9-852c-c5dd9ba41cd8
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os--claude-worktrees-crc-pass-os-sdlc/854e0e91-e925-44c9-852c-c5dd9ba41cd8.jsonl

VERDICT: CREATE
REASON: Root of the #795 regression; Facts#bound_target crash breaks every enriching pipeline node live on #796; hard floor.

## Problem #795 (commit 150727e8) removed the flat default target from `Project`. `Project#target(nil)` now raises `TargetSelectionError` on a project whose yaml has named targets. Three callers still ask for the default target. The first, `Brief::Facts#bound_target`, runs in the SubagentStart hook for every pipeline node that enriches `test_changes` or `code_changes`. Claude Code swallows a failing hook, so the agent starts with no brief and no dispatch id. ## Observed Implementation 61 (#796) on the os-sdlc target. Two test-reviewer dispatches returned without a brief. ``` os-sdlc-subagent-start < start.json ``` ``` os-sdlc-subagent-start: harness error: OsSdlc::CLI::TargetSelectionError: no target selected -- .sdlc/project.yaml defines named targets, so every run must name one (--target NAME). Available: os-backlog, cc-architect, os-sdlc, os, os-aidd-lint, os-adr ``` Backtrace: `model/project.rb:32 Project#target` ← `query/brief/facts.rb:114 Facts#bound_target` ← `Facts#codebase` ← `Facts#changes` ← `Facts#test_changes`. ## Reproduce ``` printf '{"session_id":"<id>","agent_type":"os-sdlc:test-reviewer","cwd":"<worktree>","hook_event_name":"SubagentStart"}' | os-sdlc-subagent-start ``` ## Approach Callers read the bound target from the object that knows it, never from `Project` with no name. Required work: 1. `Brief::Facts#bound_target` returns `@implementation.target` (the accessor `command/gate/run.rb:55` already uses). — check: `! grep -n "project.target(nil)" plugins/os-sdlc/lib/os_sdlc/query/brief/facts.rb` 2. `Project#status_line` no longer calls `target(nil)`: its one caller `command/cli/project_config_command.rb:91` prints one line per target for a shaped project. — check: `bin/os-sdlc` project listing on this repo exits 0 3. `Project#resolved_lint_config` takes the target name or reads it from its caller `command/autocorrect_prepass/pass.rb:35`. — check: `! grep -n "target(nil)" plugins/os-sdlc/lib/os_sdlc/model/project.rb` 4. The suite is green. — check: `ruby plugins/os-sdlc/tests/all.rb` Rejected: restore a flat default target on Project. #795 removed it on purpose; the run's target belongs to the Implementation. ## Acceptance criteria 1. Given an implementation bound to a named target on a shaped project, when the test-reviewer SubagentStart hook runs, then it exits 0 and prints the brief. 2. Given this repo's shaped yaml, when the project config listing runs, then it exits 0 and prints every target. 3. Given the full suite, when it runs, then it passes. ## Owner sketch ```ruby class OsSdlc::Brief::Facts def bound_target = @implementation.target end ``` ## Non-goals Item 1 is hand-fixed in this session on branch hotfix-795-bound-target; items 2 and 3 stay open on this ticket. ## Origin - Trigger: os-sdlc-subagent-start for test-reviewer exit 3 during /os-sdlc:implement 796 - Improvised this session: none - Chain: hook crash ← Facts#bound_target calls Project#target(nil) ← DESIGN #795 commit 150727e8 removed the flat default target - Root candidate: this ticket - Where: plugins/os-sdlc/lib/os_sdlc/query/brief/facts.rb - Session: 854e0e91-e925-44c9-852c-c5dd9ba41cd8 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os--claude-worktrees-crc-pass-os-sdlc/854e0e91-e925-44c9-852c-c5dd9ba41cd8.jsonl VERDICT: CREATE REASON: Root of the #795 regression; Facts#bound_target crash breaks every enriching pipeline node live on #796; hard floor.
Author
Owner

Item 1 hand-fixed and merged to main at b4700987 (commit ced9837e): Brief::Facts#bound_target reads @implementation.target. Items 2 and 3 (status_line, resolved_lint_config) stay open.

Item 1 hand-fixed and merged to main at b4700987 (commit ced9837e): Brief::Facts#bound_target reads @implementation.target. Items 2 and 3 (status_line, resolved_lint_config) stay open.
Author
Owner

Work started via /os-sdlc:implement on branch ticket-803 (worktree .claude/worktrees/ticket-803) for items 2-3. Finding: AutocorrectPrepass has no production caller and load_shared returns a single target, so item 3 does not crash live; the P0 crash was item 1, already on main.

Work started via /os-sdlc:implement on branch ticket-803 (worktree .claude/worktrees/ticket-803) for items 2-3. Finding: AutocorrectPrepass has no production caller and load_shared returns a single target, so item 3 does not crash live; the P0 crash was item 1, already on main.
Author
Owner

Fourth caller found live: ObjectionIntake#changed_test_paths (command/objection_intake.rb:54) called Project#target(nil); every os-sdlc-subagent-stop on this repo exited 3 (TargetSelectionError), so no pipeline agent could settle. Hotfixed to implementation.target and merged to main at 6cf71459 (suite 1333 runs, 0 failures). Implementation 65 then hit the #773 phantom dispatch on resume (dispatch 664 shadows settled 663); waiting for the stall directive.

Fourth caller found live: ObjectionIntake#changed_test_paths (command/objection_intake.rb:54) called Project#target(nil); every os-sdlc-subagent-stop on this repo exited 3 (TargetSelectionError), so no pipeline agent could settle. Hotfixed to implementation.target and merged to main at 6cf71459 (suite 1333 runs, 0 failures). Implementation 65 then hit the #773 phantom dispatch on resume (dispatch 664 shadows settled 663); waiting for the stall directive.
Author
Owner

Resolution

Done: Item 1 (Brief::Facts#bound_target) was hotfixed earlier (ced9837e). A fourth caller, ObjectionIntake#changed_test_paths, crashed every os-sdlc-subagent-stop on shaped projects; hotfixed to implementation.target at 6cf71459. Items 2-3 in 1a402f01: Project#status_line prints one line per target; Project#resolved_lint_config takes a target name.

Evidence: Implementation 65: test-reviewer passed the new test tests/model/project_shaped_listing_test.rb; behavior-verifier passed 3 times; suite 1334 runs 0 failures by hand (49.7s). The suite gate escalated only on the 47 s wall-clock budget under load 10.4, so the run was hand-finished. os-sdlc project-config --show --target os-sdlc exits 0 and prints all six targets.

Follow-ups: #813 suite gate fails green suites under load (captured, user-approved). Dropped: resolved_lint_config(target_name = nil) keeps a nil default but has no production caller (AutocorrectPrepass is test-only); target_status is public; both minor, not ticketed. #773 phantom dispatch on resume recurred (dispatch 664); already tracked.

## Resolution **Done:** Item 1 (Brief::Facts#bound_target) was hotfixed earlier (ced9837e). A fourth caller, ObjectionIntake#changed_test_paths, crashed every os-sdlc-subagent-stop on shaped projects; hotfixed to implementation.target at 6cf71459. Items 2-3 in 1a402f01: Project#status_line prints one line per target; Project#resolved_lint_config takes a target name. **Evidence:** Implementation 65: test-reviewer passed the new test tests/model/project_shaped_listing_test.rb; behavior-verifier passed 3 times; suite 1334 runs 0 failures by hand (49.7s). The suite gate escalated only on the 47 s wall-clock budget under load 10.4, so the run was hand-finished. os-sdlc project-config --show --target os-sdlc exits 0 and prints all six targets. **Follow-ups:** #813 suite gate fails green suites under load (captured, user-approved). Dropped: resolved_lint_config(target_name = nil) keeps a nil default but has no production caller (AutocorrectPrepass is test-only); target_status is public; both minor, not ticketed. #773 phantom dispatch on resume recurred (dispatch 664); already tracked.
jared closed this issue 2026-09-28 20:30:51 +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#803
No description provided.