Lesson relay: one default-lesson section that names every cop without a lesson #1424

Closed
opened 2026-10-27 16:37:59 +00:00 by jared · 2 comments
Owner

Problem

Lesson::Catalog#lesson returns a copy of the _no-lesson-yet default for each key that has no lesson file, with that key filled in for {cop}. Lesson::Relay#render removes duplicate keys, but two cops without a lesson are two keys. So one gate reply carries the full default text once for each such cop. The agent reads the same steps many times, and the reply grows with no new information. Jared's design, 2026-10-27: collect the keys with no lesson, and render the default once with every such cop named in it.

Reproduce

n/a: design change; any gate reply with two or more offenses from cops without a lesson file shows the default text twice.

Approach

The relay owns the decision that a key needs the default. The catalog only finds lesson files.

# Lesson::Relay#render
own, missing = ordered_keys(keys).partition { |key| @library.lesson(key) }
sections = own.map { |key| section_for(key) }
missing = missing.select { |key| CopKey.new(key).needs_lesson? }
sections << no_lesson_yet_section(missing) if missing.any?
return "" if sections.empty?

(["## Lint lessons"] + sections + [false_positive_section]).join("\n\n")
# _no-lesson-yet.yml
signal: >-
  These cops have no lesson yet: {cops}. ...
  • Catalog#lesson(key) returns nil for a missing file again. A new Catalog#no_lesson_yet(keys) reads _no-lesson-yet.yml once and fills {cops} with the key list. A missing _no-lesson-yet.yml still raises HarnessError naming the path.
  • The needs_lesson? rule from #1419 stays (no default for autocorrectable Layout/Style cops).
  • The seen_keys rule applies to the default as it does to each lesson: when every missing key was seen in an earlier reply, the section shows only the signal line with the cop names.
  • Before the move, check each direct caller of Catalog#lesson (offense_directive.rb, gate/run.rb, detector/run.rb, dispatch/brief.rb). A caller that needs the default must get it through the relay or no_lesson_yet.

Required work:

  1. Replace {cop} with {cops} in the default lesson file — check: present {cops} in plugins/os-sdlc/lint-lessons/_no-lesson-yet.yml
  2. Add the one-section default to the relay — check: present no_lesson_yet in plugins/os-sdlc/lib/os_sdlc/lesson/relay.rb

Rejected: keep a default copy per key — repeats the same steps in one reply.
Rejected: remove repeated default text inside the relay by comparing text — hides the design; the relay already knows which keys have no lesson.

Acceptance criteria

  1. Given one gate reply with offenses from two cops that have no lesson file, when the relay renders, then the default lesson appears once and names both cops.
  2. Given offenses from one cop with a lesson and one cop without, when the relay renders, then the reply has the cop's own lesson, one default section naming the other cop, and the false-positive lesson once.
  3. Given only offenses from autocorrectable Layout or Style cops without a lesson file, when the relay renders, then no default section appears.
  4. Given a key with no lesson file, when Catalog#lesson is called, then it returns nil.
  5. Given a lessons directory without _no-lesson-yet.yml and a batch with a cop that needs the default, when the relay renders, then it raises an error that names the missing path.
  6. Given every missing key was seen in an earlier reply, when the relay renders, then the default section shows only its signal line with the cop names.

Owner sketch

class OsSdlc::Lesson::Relay
  def render(keys)
end

class OsSdlc::Lesson::Catalog
  def lesson(key)
  def no_lesson_yet(keys)
end

Origin

  • Trigger: Jared's design note on 2026-10-27, after #1414 and #1419 shipped a default copy per key.
  • Improvised this session: none
  • Chain: repeated default text in one gate reply ← relay removes duplicates by key only ← Catalog#lesson fills one default per key (plugins/os-sdlc/lib/os_sdlc/lesson/catalog.rb) ← DESIGN (#1414)
  • Root candidate: none found; searched _no-lesson-yet, no_lesson_yet, Relay#render, {cops}, default lesson once, Lesson::Catalog
  • Where: plugins/os-sdlc/lib/os_sdlc/lesson/relay.rb, plugins/os-sdlc/lib/os_sdlc/lesson/catalog.rb
  • Session: 0b4d7950-df1c-4eba-a561-ccc19b386a9a
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/0b4d7950-df1c-4eba-a561-ccc19b386a9a.jsonl

VERDICT: CREATE

## Problem `Lesson::Catalog#lesson` returns a copy of the `_no-lesson-yet` default for each key that has no lesson file, with that key filled in for `{cop}`. `Lesson::Relay#render` removes duplicate keys, but two cops without a lesson are two keys. So one gate reply carries the full default text once for each such cop. The agent reads the same steps many times, and the reply grows with no new information. Jared's design, 2026-10-27: collect the keys with no lesson, and render the default once with every such cop named in it. ## Reproduce n/a: design change; any gate reply with two or more offenses from cops without a lesson file shows the default text twice. ## Approach The relay owns the decision that a key needs the default. The catalog only finds lesson files. ```ruby # Lesson::Relay#render own, missing = ordered_keys(keys).partition { |key| @library.lesson(key) } sections = own.map { |key| section_for(key) } missing = missing.select { |key| CopKey.new(key).needs_lesson? } sections << no_lesson_yet_section(missing) if missing.any? return "" if sections.empty? (["## Lint lessons"] + sections + [false_positive_section]).join("\n\n") ``` ```yaml # _no-lesson-yet.yml signal: >- These cops have no lesson yet: {cops}. ... ``` - `Catalog#lesson(key)` returns nil for a missing file again. A new `Catalog#no_lesson_yet(keys)` reads `_no-lesson-yet.yml` once and fills `{cops}` with the key list. A missing `_no-lesson-yet.yml` still raises HarnessError naming the path. - The `needs_lesson?` rule from #1419 stays (no default for autocorrectable Layout/Style cops). - The `seen_keys` rule applies to the default as it does to each lesson: when every missing key was seen in an earlier reply, the section shows only the signal line with the cop names. - Before the move, check each direct caller of `Catalog#lesson` (`offense_directive.rb`, `gate/run.rb`, `detector/run.rb`, `dispatch/brief.rb`). A caller that needs the default must get it through the relay or `no_lesson_yet`. Required work: 1. Replace `{cop}` with `{cops}` in the default lesson file — check: present `{cops}` in `plugins/os-sdlc/lint-lessons/_no-lesson-yet.yml` 2. Add the one-section default to the relay — check: present `no_lesson_yet` in `plugins/os-sdlc/lib/os_sdlc/lesson/relay.rb` Rejected: keep a default copy per key — repeats the same steps in one reply. Rejected: remove repeated default text inside the relay by comparing text — hides the design; the relay already knows which keys have no lesson. ## Acceptance criteria 1. Given one gate reply with offenses from two cops that have no lesson file, when the relay renders, then the default lesson appears once and names both cops. 2. Given offenses from one cop with a lesson and one cop without, when the relay renders, then the reply has the cop's own lesson, one default section naming the other cop, and the false-positive lesson once. 3. Given only offenses from autocorrectable Layout or Style cops without a lesson file, when the relay renders, then no default section appears. 4. Given a key with no lesson file, when `Catalog#lesson` is called, then it returns nil. 5. Given a lessons directory without `_no-lesson-yet.yml` and a batch with a cop that needs the default, when the relay renders, then it raises an error that names the missing path. 6. Given every missing key was seen in an earlier reply, when the relay renders, then the default section shows only its signal line with the cop names. ## Owner sketch ```ruby class OsSdlc::Lesson::Relay def render(keys) end class OsSdlc::Lesson::Catalog def lesson(key) def no_lesson_yet(keys) end ``` ## Origin - Trigger: Jared's design note on 2026-10-27, after #1414 and #1419 shipped a default copy per key. - Improvised this session: none - Chain: repeated default text in one gate reply ← relay removes duplicates by key only ← `Catalog#lesson` fills one default per key (`plugins/os-sdlc/lib/os_sdlc/lesson/catalog.rb`) ← DESIGN (#1414) - Root candidate: none found; searched _no-lesson-yet, no_lesson_yet, Relay#render, {cops}, default lesson once, Lesson::Catalog - Where: `plugins/os-sdlc/lib/os_sdlc/lesson/relay.rb`, `plugins/os-sdlc/lib/os_sdlc/lesson/catalog.rb` - Session: 0b4d7950-df1c-4eba-a561-ccc19b386a9a - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/0b4d7950-df1c-4eba-a561-ccc19b386a9a.jsonl VERDICT: CREATE
Author
Owner

Work started on branch ticket-1424 through /os-sdlc:implement (poodr-ticket-implementation).

Work started on branch ticket-1424 through /os-sdlc:implement (poodr-ticket-implementation).
Author
Owner

Resolution

Done: Lesson::Relay renders one no-lesson-yet section that names every cop without a lesson ({cops}); Catalog#lesson returns nil for a missing file; Catalog#no_lesson_yet(keys) reads the default once and raises HarnessError naming the path when it is missing; needs_lesson? rule kept; seen_keys shortens the default to its signal line.

Evidence: Implementation 189 merge-ready, branch ticket-1424; Runner commit plus orchestrator test fix 0874b58e (catalog tests moved to no_lesson_yet; seen-key refutes scoped to the default section). Full os-sdlc suite 2773 runs, 0 failures; suite gate green; contract-auditor pass.

Follow-ups: Dropped: test_every_enabled_sdlc_cop_has_its_own_lesson_or_gets_the_default is now always true (no Sdlc cop is format-only); kept as the contract statement, no ticket. Workflow note for the session report: decomposition did not list catalog_test.rb as retired tests when Catalog#lesson changed its return contract.

## Resolution **Done:** Lesson::Relay renders one no-lesson-yet section that names every cop without a lesson ({cops}); Catalog#lesson returns nil for a missing file; Catalog#no_lesson_yet(keys) reads the default once and raises HarnessError naming the path when it is missing; needs_lesson? rule kept; seen_keys shortens the default to its signal line. **Evidence:** Implementation 189 merge-ready, branch ticket-1424; Runner commit plus orchestrator test fix 0874b58e (catalog tests moved to no_lesson_yet; seen-key refutes scoped to the default section). Full os-sdlc suite 2773 runs, 0 failures; suite gate green; contract-auditor pass. **Follow-ups:** Dropped: test_every_enabled_sdlc_cop_has_its_own_lesson_or_gets_the_default is now always true (no Sdlc cop is format-only); kept as the contract statement, no ticket. Workflow note for the session report: decomposition did not list catalog_test.rb as retired tests when Catalog#lesson changed its return contract.
jared closed this issue 2026-10-27 17:05:59 +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#1424
No description provided.