Lesson catalog: default lesson for Sdlc cops with no lesson, and a false-positive lesson per batch #1414
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
rule-proposal
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#1414
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Lesson::Catalog#lesson(key)returns nil when a cop has no lesson file.Lesson::Relay#ordered_keys(lesson/relay.rb:35) then drops the cop without a message. Six enabled Sdlc cops have no lesson today: HandRolledFetch, TautologicalAssertion, TestMethodAfterPrivate, UnreachableBranchAfterGuard, InstanceVariableAssumption, SimulatedPolymorphism. An agent that hits one of them gets no guidance and can fix the smoke instead of the fire. Also, no lesson tells an agent what to do when it thinks a hit is a false positive.Reproduce
n/a: design gap; no lesson text reaches the agent for the six cops above.
Approach
Two data files carry the process text, so a process change edits one file.
_no-lesson-yet.ymlis found by file-name convention. Catalog fills in the cop name. The text refers to "the offense reported above"; the offense site is not passed in, because Relay gets keys only._no-lesson-yet.ymlsteps: capture a ticket with the cop, and the file:line and snippet of the offense reported above; run/os-sdlc:smell-analysison the cop and that offense to find the smoke and the fire; update the ticket; tell Jared._false-positive.ymltext: do not suppress; capture a ticket with the snippet and one true positive from the cop's tests; smell-analyze both; answer (a) what distinguishes this hit (sharpen the matcher and the lesson) and (b) is it smoke for a different problem (new cop and lesson). It includes the sentence "The cop's pattern matched but its intent did not; something unusual in the code often caused the match." It agrees with the "No false positives" section ofplugins/os-sdlc/skills/mine-lints/references/implement-cop.md._no-lesson-yet.ymlor_false-positive.ymlraises HarnessError that names the path.Sdlc/*(Layout, Metrics, detector keys) keep today's nil result.Required work:
smell-analysisinplugins/os-sdlc/lint-lessons/_no-lesson-yet.ymlintent did notinplugins/os-sdlc/lint-lessons/_false-positive.ymlRejected: thread the offense file:line into the default lesson — needs new plumbing through
OffenseDirectiveandGate::Run; the gate output already shows the site above the lessons.Rejected: default lesson for every key — a ticket per Layout offense is noise.
Acceptance criteria
Sdlc/*cop with no lesson file, when the catalog looks it up, then it returns the default lesson, and the lesson text names that cop.Sdlc/*with no lesson file, when the catalog looks it up, then it returns nil._no-lesson-yet.yml, when the catalog looks up anSdlc/*cop that has no lesson, then it raises an error that names the missing path..rubocop.yml, when every enabledSdlc/*cop is looked up in the default catalog, then each one resolves to a lesson._no-lesson-yet.ymland_false-positive.yml, when each is loaded, then both passLesson.fromvalidation.Owner sketch
Non-goals
Origin
.cc-os/os/handoffs/2026-10-27-1542-lint-cops-track.md, increment 1; blast radius 2026-10-27.Relay#ordered_keysdrops nil lessons (plugins/os-sdlc/lib/os_sdlc/lesson/relay.rb:35) ←Catalog#lessonreturns nil for a missing file (plugins/os-sdlc/lib/os_sdlc/lesson/catalog.rb:11-15) ← DESIGN (lesson/catalog.rb)plugins/os-sdlc/lib/os_sdlc/lesson/catalog.rb,plugins/os-sdlc/lib/os_sdlc/lesson/relay.rbVERDICT: CREATE
Work started on branch ticket-1414 through /os-sdlc:implement (poodr-ticket-implementation).
Resolution
Done: Lesson::Catalog returns the _no-lesson-yet default (cop name filled in) for an Sdlc/* key with no lesson file and raises HarnessError naming the path when a required default file is missing; Lesson::Relay appends the _false-positive lesson once per batch when any lesson exists. Two data files added under plugins/os-sdlc/lint-lessons/.
Evidence: Implementation 182 complete (13 dispatches, 20 min 38 s); commits
ca4ebfa8(production, Runner) and1f148fc6(test fixtures hand-fixed after a programmer test-defect escalation and reroute); os-sdlc suite green twice at suite-check (dispatches 1603, 1614); contract-auditor pass (1615).Follow-ups: #1419: widen the default lesson to every acted-on offense except autocorrectable Layout/Style cops (Jared ruled 2026-10-27).