Replace the lint_config default sentinel with Project::LintConfig [capture] #573

Closed
opened 2026-09-14 13:45:08 +00:00 by jared · 2 comments
Owner

Context

The responsibility audit found that "is a lint config configured?" is
answered two ways. Project#resolved_lint_config compares the value to the
default string .rubocop.yml. LintWorklistRubocopConfig#custom_lint_config?
compares the value to root/.rubocop.yml. The sentinel and a legal user value
are the same string. Callers that would delegate:
Project#resolved_lint_config, AutocorrectPrepass#root_lint_config,
LintWorklistRubocopConfig#custom_lint_config?. This changes ATTR_DEFAULTS;
decide it before Project::Layout (#570) or Project::ArtifactDir move.

Observed

lib/os_sdlc/project.rb:86 return nil if lint_config ==
self.class::DEFAULT_LINT_CONFIG
lib/os_sdlc/project.rb:88 File.expand_path(lint_config,
root)
lib/os_sdlc/lint_worklist_rubocop_config.rb:50 @lint_config &&
@lint_config != target_config_path
lib/os_sdlc/lint_worklist_rubocop_config.rb:54 File.join(@project_root, ".
rubocop.yml")

Project.new(name: "x", root: "/tmp", lint_config:
".rubocop.yml").resolved_lint_config returns nil, while lint_config:
"cfg/.rubocop.yml" returns /tmp/cfg/.rubocop.yml.

Reproduce

cd plugins/os-sdlc && ruby -Ilib -e 'require "os_sdlc/project"; p
OsSdlc::Project.new(name: "x", root: "/tmp", lint_config: ".rubocop.yml").
resolved_lint_config'

Expected

Minitest snippet test_explicit_default_lint_config_is_configured asserting
that an explicit lint_config: ".rubocop.yml" resolves to /tmp/.rubocop.yml,
and that Project::LintConfig.new(nil, root: "/tmp").configured? is false.

Illustration

illustration, not spec

class Project::LintConfig #
lib/os_sdlc/project/lint_config.rb
def initialize(value, root:) = (@value, @root = value, root)

def configured? = !@value.nil?            # absence is nil, not a magic 

string
def path = configured? ? File.expand_path(@value, @root) : nil
end

Origin

• Trigger: /os-sdlc:responsibility-audit run 13 on plugins/os-
sdlc/lib/os_sdlc/project.rb, 2026-09-14
• Improvised this session: none
• Chain: two readers disagree ← default sentinel equals a legal value ←
DEFAULT_LINT_CONFIG used as an absence marker (DESIGN, lib/os_sdlc/project.
rb:86)
• Root candidate: this ticket
• Where: lib/os_sdlc/project.rb
• Session: 23a382aa-f78e-495f-9cbd-0934e163b37d
• Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/23a382aa-
f78e-495f-9cbd-0934e163b37d.jsonl

Tasks

  • Add Project::LintConfig at lib/os_sdlc/project/lint_config.rb with configured? and path.
  • ATTR_DEFAULTS[:lint_config] becomes nil; absence is nil, not .rubocop.yml.
  • Project#resolved_lint_config delegates to Project::LintConfig#path.
  • LintWorklistRubocopConfig#custom_lint_config? asks Project::LintConfig#configured?.

Acceptance criteria

  • Project.new(name: "x", root: "/tmp", lint_config: ".rubocop.yml").resolved_lint_config returns /tmp/.rubocop.yml.
  • Project.new(name: "x", root: "/tmp").resolved_lint_config returns nil.
  • Project::LintConfig.new(nil, root: "/tmp").configured? is false.
  • A configured lint_config that does not exist on disk still raises HarnessError (ADR-0091).

Structural follow-through

  • AutocorrectPrepass#root_lint_config delegates to Project::LintConfig.
  • Check: rg -n 'DEFAULT_LINT_CONFIG' plugins/os-sdlc/lib returns nothing.

ADR case

Amends ADR-0091 (Project::LintConfig becomes the sole sentinel-vs-custom resolver) and ADR-0074 (lint_config default is nil). Create the amending ADR when this ticket lands.

Batch

Map: #570. Position 2 of 4. Follows #571. Followed by #574. Map: maps/poodr-ticket-implementation.yaml.

## Context The responsibility audit found that "is a lint config configured?" is answered two ways. Project#resolved_lint_config compares the value to the default string .rubocop.yml. LintWorklistRubocopConfig#custom_lint_config? compares the value to root/.rubocop.yml. The sentinel and a legal user value are the same string. Callers that would delegate: Project#resolved_lint_config, AutocorrectPrepass#root_lint_config, LintWorklistRubocopConfig#custom_lint_config?. This changes ATTR_DEFAULTS; decide it before Project::Layout (#570) or Project::ArtifactDir move. ## Observed lib/os_sdlc/project.rb:86 return nil if lint_config == self.class::DEFAULT_LINT_CONFIG lib/os_sdlc/project.rb:88 File.expand_path(lint_config, root) lib/os_sdlc/lint_worklist_rubocop_config.rb:50 @lint_config && @lint_config != target_config_path lib/os_sdlc/lint_worklist_rubocop_config.rb:54 File.join(@project_root, ". rubocop.yml") Project.new(name: "x", root: "/tmp", lint_config: ".rubocop.yml").resolved_lint_config returns nil, while lint_config: "cfg/.rubocop.yml" returns /tmp/cfg/.rubocop.yml. ## Reproduce cd plugins/os-sdlc && ruby -Ilib -e 'require "os_sdlc/project"; p OsSdlc::Project.new(name: "x", root: "/tmp", lint_config: ".rubocop.yml"). resolved_lint_config' ## Expected Minitest snippet test_explicit_default_lint_config_is_configured asserting that an explicit lint_config: ".rubocop.yml" resolves to /tmp/.rubocop.yml, and that Project::LintConfig.new(nil, root: "/tmp").configured? is false. ## Illustration # illustration, not spec class Project::LintConfig # lib/os_sdlc/project/lint_config.rb def initialize(value, root:) = (@value, @root = value, root) def configured? = !@value.nil? # absence is nil, not a magic string def path = configured? ? File.expand_path(@value, @root) : nil end ## Origin • Trigger: /os-sdlc:responsibility-audit run 13 on plugins/os- sdlc/lib/os_sdlc/project.rb, 2026-09-14 • Improvised this session: none • Chain: two readers disagree ← default sentinel equals a legal value ← DEFAULT_LINT_CONFIG used as an absence marker (DESIGN, lib/os_sdlc/project. rb:86) • Root candidate: this ticket • Where: lib/os_sdlc/project.rb • Session: 23a382aa-f78e-495f-9cbd-0934e163b37d • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/23a382aa- f78e-495f-9cbd-0934e163b37d.jsonl ## Tasks - [ ] Add `Project::LintConfig` at lib/os_sdlc/project/lint_config.rb with `configured?` and `path`. - [ ] `ATTR_DEFAULTS[:lint_config]` becomes `nil`; absence is `nil`, not `.rubocop.yml`. - [ ] `Project#resolved_lint_config` delegates to `Project::LintConfig#path`. - [ ] `LintWorklistRubocopConfig#custom_lint_config?` asks `Project::LintConfig#configured?`. ## Acceptance criteria - `Project.new(name: "x", root: "/tmp", lint_config: ".rubocop.yml").resolved_lint_config` returns `/tmp/.rubocop.yml`. - `Project.new(name: "x", root: "/tmp").resolved_lint_config` returns `nil`. - `Project::LintConfig.new(nil, root: "/tmp").configured?` is false. - A configured `lint_config` that does not exist on disk still raises `HarnessError` (ADR-0091). ## Structural follow-through - `AutocorrectPrepass#root_lint_config` delegates to `Project::LintConfig`. - Check: `rg -n 'DEFAULT_LINT_CONFIG' plugins/os-sdlc/lib` returns nothing. ## ADR case Amends ADR-0091 (`Project::LintConfig` becomes the sole sentinel-vs-custom resolver) and ADR-0074 (`lint_config` default is `nil`). Create the amending ADR when this ticket lands. ## Batch Map: #570. Position 2 of 4. Follows #571. Followed by #574. Map: maps/poodr-ticket-implementation.yaml.
Author
Owner

Work started via /os-sdlc:responsibility-refactor batch 2 (map #570). Branch: ticket-573, worktree .claude/worktrees/ticket-573, map poodr-ticket-implementation.yaml.

Work started via /os-sdlc:responsibility-refactor batch 2 (map #570). Branch: ticket-573, worktree .claude/worktrees/ticket-573, map poodr-ticket-implementation.yaml.
Author
Owner

Resolution

Done: Project::LintConfig (project/lint_config.rb) is the sole lint-config resolver: configured? is non-nil, path expands against root. Project#resolved_lint_config and LintWorklistRubocopConfig#custom_lint_config? delegate; ATTR_DEFAULTS[:lint_config] is nil; DEFAULT_LINT_CONFIG removed. ADR-0179 amends ADR-0091/0074.

Evidence: Merged to main 058f20d (code bc4f4cf). Suite 1223 runs, 0 failures; rubocop clean on 5 touched files; pre-tool-use hook smoke test still denies. Pipeline: 16 dispatches, 1529 s, implementation_complete after 3 contract-auditor rounds. Deviations: hand-fixed test-side conflict (shared DEFAULT_ATTRS helper asserted the old default; made nil-aware), hand-settled a reused dispatch 91 after the gate went green, moved LintConfig from a nested class in project.rb to its own file per structural follow-through. Position 2 of 4 on map #570.

Follow-ups: none. Structural follow-through done: rg DEFAULT_LINT_CONFIG under lib returns nothing; AutocorrectPrepass#root_lint_config already delegates through resolved_lint_config. Observed once: autocorrect_prepass_test is order-dependent in the full suite (passes alone, 6 vs 5 failures across two runs); pre-existing, not ticketed.

## Resolution **Done:** Project::LintConfig (project/lint_config.rb) is the sole lint-config resolver: configured? is non-nil, path expands against root. Project#resolved_lint_config and LintWorklistRubocopConfig#custom_lint_config? delegate; ATTR_DEFAULTS[:lint_config] is nil; DEFAULT_LINT_CONFIG removed. ADR-0179 amends ADR-0091/0074. **Evidence:** Merged to main 058f20d (code bc4f4cf). Suite 1223 runs, 0 failures; rubocop clean on 5 touched files; pre-tool-use hook smoke test still denies. Pipeline: 16 dispatches, 1529 s, implementation_complete after 3 contract-auditor rounds. Deviations: hand-fixed test-side conflict (shared DEFAULT_ATTRS helper asserted the old default; made nil-aware), hand-settled a reused dispatch 91 after the gate went green, moved LintConfig from a nested class in project.rb to its own file per structural follow-through. Position 2 of 4 on map #570. **Follow-ups:** none. Structural follow-through done: rg DEFAULT_LINT_CONFIG under lib returns nothing; AutocorrectPrepass#root_lint_config already delegates through resolved_lint_config. Observed once: autocorrect_prepass_test is order-dependent in the full suite (passes alone, 6 vs 5 failures across two runs); pre-existing, not ticketed.
jared closed this issue 2026-09-14 14:46:24 +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#573
No description provided.