Project::Target carries name, test, lint, tracker; Project#targets lists every target from .sdlc/project.yaml #791

Closed
opened 2026-09-28 14:17:02 +00:00 by jared · 3 comments
Owner

Problem

A Project describes one target only. The yaml loader merges the shared keys into the selected section and discards all other targets (ProjectTargets#attrs, Yaml::Document#attrs). No object can answer "which targets does this project have". R5 says a Project has many targets, and each target is a value with name, path, test, lint, tracker.

Reproduce

n/a: new behavior. Failing test: Project::Yaml.load(dir, target: "b").targets.map(&:name) for a yaml with targets a and b returns ["a", "b"]. Today it raises NoMethodError.

Approach

Files (7): model/project/target.rb, model/project.rb, parser/project/yaml/document.rb, model/project_targets.rb, adapter/project/yaml.rb, tests/model/project_targets_test.rb, tests/model/project/targets_test.rb (new).
ADRs cited: ADR-0088 (named targets; the precedence is target key, then shared key, then default; name stays top-level), ADR-0056 (tracker), ADR-0167 (Data.define for values), ADR-0210 rule 1-2 (target path, default "."), ADR-0192 item 2 (Test and Lint value objects). ADR-0238 (Accepted, 2026-09-28) already amends ADR-0088 "the Project Data member list does not change"; no ADR is written in this ticket.

Required work:

  1. Project::Target = Data.define(:name, :path, :test, :lint, :tracker). The defaults are path ".", Test.new, Lint.new, and tracker nil. include? and relative do not change. — check: ruby plugins/os-sdlc/tests/model/project/targets_test.rb
  2. Project gains a targets member (an Array of Target). The yaml side builds it at load: one Target per targets: section, with the shared top-level keys merged under the section keys. A yaml with no targets: map gives one Target named after the project, with its path from the top-level path (default "."). The member is set at construction. Do not memoize it with @targets ||=: Project is a frozen Data, and that raises FrozenError (confirmed 2026-09-28). — check: same test
  3. The per-target tracker is section["tracker"] || data["tracker"]. — check: same test
  4. The existing members target, test, and lint (the selected target) stay unchanged in this ticket. T7 removes them. — check: ruby plugins/os-sdlc/tests/all.rb

Rejected: def targets = @targets ||= gather_targets on Project. Data instances are frozen, so this raises FrozenError.
Rejected: make Project a plain class so that it can memoize. It is a value with no identity (ADR-0167 role test 2).

Acceptance criteria

  1. Given a yaml with top-level test_framework: minitest and targets a (path pa) and b (path pb, test_command: rake b), when the project loads, then targets names a and b, b has test command rake b, and both have framework minitest.
  2. Given a flat yaml name: widgets with no targets: key, when the project loads, then targets is one target named widgets with path ..
  3. Given a target section with its own tracker:, when the project loads, then that target's tracker is the section value. Other targets get the top-level tracker.
  4. Given the existing multi-target and flat load tests, when the suite runs, then they pass without edits.

Owner sketch

OsSdlc::Project::Target = Data.define(:name, :path, :test, :lint, :tracker)
OsSdlc::Project = Data.define(:name, :path, :tracker, :target, :test, :lint, :map, :commit_on_complete, :targets)
class OsSdlc::Project::Yaml::Document
  def targets(root:)   # [Target], shared keys merged into each section; flat -> [Target(name: name, path: ...)]
end

Non-goals

Persistence of targets (T3). Removal of the selected-target members, FlatAttrs, and ProjectTargets (T7). Target-relative lint paths (T6).

Origin

  • Trigger: user ruling R5 (Project has a root and many targets)
  • Improvised this session: none
  • Chain: no object lists a project's targets ← the yaml loader resolves one target and discards the rest (DESIGN: ADR-0088 rejected alternative "Extending the Project Data object with a targets member")
  • Where: plugins/os-sdlc/lib/os_sdlc/model/project_targets.rb, parser/project/yaml/document.rb
  • Session: bb36e383-017d-486e-809f-7ce25d151e61
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os--claude-worktrees-crc-pass-os-sdlc/bb36e383-017d-486e-809f-7ce25d151e61.jsonl
## Problem A Project describes one target only. The yaml loader merges the shared keys into the selected section and discards all other targets (`ProjectTargets#attrs`, `Yaml::Document#attrs`). No object can answer "which targets does this project have". R5 says a Project has many targets, and each target is a value with `name, path, test, lint, tracker`. ## Reproduce n/a: new behavior. Failing test: `Project::Yaml.load(dir, target: "b").targets.map(&:name)` for a yaml with targets `a` and `b` returns `["a", "b"]`. Today it raises NoMethodError. ## Approach Files (7): model/project/target.rb, model/project.rb, parser/project/yaml/document.rb, model/project_targets.rb, adapter/project/yaml.rb, tests/model/project_targets_test.rb, tests/model/project/targets_test.rb (new). ADRs cited: ADR-0088 (named targets; the precedence is target key, then shared key, then default; `name` stays top-level), ADR-0056 (tracker), ADR-0167 (Data.define for values), ADR-0210 rule 1-2 (target `path`, default "."), ADR-0192 item 2 (Test and Lint value objects). ADR-0238 (Accepted, 2026-09-28) already amends ADR-0088 "the Project Data member list does not change"; no ADR is written in this ticket. Required work: 1. `Project::Target = Data.define(:name, :path, :test, :lint, :tracker)`. The defaults are path ".", `Test.new`, `Lint.new`, and tracker nil. `include?` and `relative` do not change. — check: `ruby plugins/os-sdlc/tests/model/project/targets_test.rb` 2. `Project` gains a `targets` member (an Array of Target). The yaml side builds it at load: one Target per `targets:` section, with the shared top-level keys merged under the section keys. A yaml with no `targets:` map gives one Target named after the project, with its path from the top-level `path` (default "."). The member is set at construction. Do not memoize it with `@targets ||=`: Project is a frozen Data, and that raises FrozenError (confirmed 2026-09-28). — check: same test 3. The per-target tracker is `section["tracker"] || data["tracker"]`. — check: same test 4. The existing members `target`, `test`, and `lint` (the selected target) stay unchanged in this ticket. T7 removes them. — check: `ruby plugins/os-sdlc/tests/all.rb` Rejected: `def targets = @targets ||= gather_targets` on Project. Data instances are frozen, so this raises FrozenError. Rejected: make Project a plain class so that it can memoize. It is a value with no identity (ADR-0167 role test 2). ## Acceptance criteria 1. Given a yaml with top-level `test_framework: minitest` and targets `a` (path `pa`) and `b` (path `pb`, `test_command: rake b`), when the project loads, then `targets` names `a` and `b`, `b` has test command `rake b`, and both have framework `minitest`. 2. Given a flat yaml `name: widgets` with no `targets:` key, when the project loads, then `targets` is one target named `widgets` with path `.`. 3. Given a target section with its own `tracker:`, when the project loads, then that target's tracker is the section value. Other targets get the top-level tracker. 4. Given the existing multi-target and flat load tests, when the suite runs, then they pass without edits. ## Owner sketch ```ruby OsSdlc::Project::Target = Data.define(:name, :path, :test, :lint, :tracker) OsSdlc::Project = Data.define(:name, :path, :tracker, :target, :test, :lint, :map, :commit_on_complete, :targets) class OsSdlc::Project::Yaml::Document def targets(root:) # [Target], shared keys merged into each section; flat -> [Target(name: name, path: ...)] end ``` ## Non-goals Persistence of targets (T3). Removal of the selected-target members, FlatAttrs, and ProjectTargets (T7). Target-relative lint paths (T6). ## Origin - Trigger: user ruling R5 (Project has a root and many targets) - Improvised this session: none - Chain: no object lists a project's targets ← the yaml loader resolves one target and discards the rest (DESIGN: ADR-0088 rejected alternative "Extending the Project Data object with a targets member") - Where: plugins/os-sdlc/lib/os_sdlc/model/project_targets.rb, parser/project/yaml/document.rb - Session: bb36e383-017d-486e-809f-7ce25d151e61 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os--claude-worktrees-crc-pass-os-sdlc/bb36e383-017d-486e-809f-7ce25d151e61.jsonl
Author
Owner

Work started on branch ticket-791 via /os-sdlc:implement (session bb36e383).

Work started on branch ticket-791 via /os-sdlc:implement (session bb36e383).
Author
Owner

Escalation on implementation 55: change-auditor failed on the 'ADR to write first' precondition. ADR-0238 already exists and amends ADR-0088. Ticket text corrected; re-entering with a fresh implementation-open.

Escalation on implementation 55: change-auditor failed on the 'ADR to write first' precondition. ADR-0238 already exists and amends ADR-0088. Ticket text corrected; re-entering with a fresh implementation-open.
Author
Owner

Resolution

Done: Project::Target is Data(name, path, test, lint, tracker); Project has a targets member built at load with one Target per yaml section, shared keys merged under section keys, per-target tracker falling back to the top-level tracker; a flat yaml yields one target named after the project with path '.'. Selected-target members unchanged.

Evidence: os-sdlc implementation 56 on branch ticket-791 (implementation 55 escalated on a stale ADR clause in the ticket, corrected, reopened): 13 dispatches, 1 suite-check retry repaired; final suite-check pass; contract-auditor pass; new tests/model/project/targets_test.rb; commit eae175b8

Follow-ups: to_flat_h now emits target_name into the stored snapshot; T3 (#792) reshapes that blob per ADR-0238

## Resolution **Done:** Project::Target is Data(name, path, test, lint, tracker); Project has a targets member built at load with one Target per yaml section, shared keys merged under section keys, per-target tracker falling back to the top-level tracker; a flat yaml yields one target named after the project with path '.'. Selected-target members unchanged. **Evidence:** os-sdlc implementation 56 on branch ticket-791 (implementation 55 escalated on a stale ADR clause in the ticket, corrected, reopened): 13 dispatches, 1 suite-check retry repaired; final suite-check pass; contract-auditor pass; new tests/model/project/targets_test.rb; commit eae175b8 **Follow-ups:** to_flat_h now emits target_name into the stored snapshot; T3 (#792) reshapes that blob per ADR-0238
jared closed this issue 2026-09-28 14:49:56 +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#791
No description provided.