Widen RegistryOutsideEntryPoint to root lookups by id on a model class #1450
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#1450
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
The cop
Sdlc/Sequel/RegistryOutsideEntryPointdoes not flag a Sequel model lookupModel[id]. That lookup is the same smell as the registry lookup, without the registry. Today the lookup is copied into seven places (Acceptance,AcceptanceCheckx2,Placing,ReportLookup,Abandon,RerouteIntake), and it causes three defects in the acceptance commands:retired-tests-checkandrequired-work-checklook up the same row twice.mutation-check 999andunchanged-check 999printretired-tests-check no implementation 999.retired_tests,required_workandunchangeduse the Boot root, not the row'sworktree_root(Implementation#work_root(fallback)inimplementation/targeting.rb). Onlymutation_checkuses it.Analysis:
docs/lint-lessons/analyses/sdlc-sequel-registry-outside-entry-point.md. Blocker #1381 landed (maine2588c72).Ruling (2026-10-28)
Jared ruled. This replaces the earlier
Implementation.fetch!(id, command)plan and the entry-point question entirely. A command class looks up its row once, ininitialize. The cop allowsModel[...]only insideinitialize. No Exclude lists, no path lists, no entry-point classification. Moving the otherTicketShape.new(ticket_body)callers toImplementation#ticket_shapeis out of scope (follow-up, not filed).Approach
Acceptanceloads the row in its constructor and raises its own error.AcceptanceCheckis deleted and its logic moves intoAcceptance. The cop gets a shape rule in the matcher: a constant that resolves to a Sequel model, sent[], outside adef initialize.ENV[],Thread.current[], constant hashes,Sequel[:t]stay clean.Lookups that live elsewhere today move into
initialize:implementation/abandon.rb:14,implementation/report_lookup.rb:7(becomes a class),implementation/reroute_intake.rb:30-31(the lazy@implementation ||=),dispatch/placing.rb:13(Dispatch[...]; the class takesdispatch_idin its constructor).Hits of the widened rule under
plugins/os-sdlc/lib(found by grep for model-constant[]receivers; every one is covered below):implementation/acceptance.rb:33-> moves intoinitialize.implementation/acceptance_check.rb:7,:13-> file deleted.implementation/abandon.rb:14,implementation/report_lookup.rb:7,implementation/reroute_intake.rb:30-31,dispatch/placing.rb:13-> move intoinitialize.Accountinghas no direct lookup (it callsReportLookup).Test files that call
AcceptanceCheckmove toAcceptance.new(id, root, io).<check>:plugins/os-sdlc/tests/implementation/acceptance_check_retired_tests_test.rb,plugins/os-sdlc/tests/command/implementation/reroute_refresh_test.rb,plugins/os-sdlc/tests/command/required_work_typed_check_test.rb. Boot call siteslib/os_sdlc/boot/commands.rb:50,58,62,66change toAcceptance.new(id, root, io).<check>; line 22 changes toPlacing.new(io, dispatch_id).place(root, mode).Required work:
def initialize(id, root, io)inplugins/os-sdlc/lib/os_sdlc/implementation/acceptance.rbdef implementation_forinplugins/os-sdlc/lib/os_sdlc/implementation/acceptance.rbAcceptanceCheckinplugins/os-sdlc/lib/**/*.rbAcceptanceCheck.inplugins/os-sdlc/tests/**/*.rbdef ticket_shapeinplugins/os-sdlc/lib/os_sdlc/implementation.rbAcceptance.new(io)inplugins/os-sdlc/lib/os_sdlc/boot/commands.rbPlacing.new(io).placeinplugins/os-sdlc/lib/os_sdlc/boot/commands.rb:initializeinplugins/os-sdlc/lib/os_sdlc/cops/sequel/registry_outside_entry_point.rbImplementation[inplugins/os-sdlc/tests/cops/registry_outside_entry_point_test.rb@implementation = Implementation[id]inplugins/os-sdlc/lib/os_sdlc/implementation/abandon.rbdef initialize(id)inplugins/os-sdlc/lib/os_sdlc/implementation/report_lookup.rb@implementation ||=inplugins/os-sdlc/lib/os_sdlc/implementation/reroute_intake.rbdef initialize(io, dispatch_id)inplugins/os-sdlc/lib/os_sdlc/dispatch/placing.rbAcceptance criteria
retired-tests-check,required-work-check,mutation-check,unchanged-checkrun for an existing id, when the command runs, then the implementation row is selected exactly once (today the first two select it twice).mutation-check 999orunchanged-check 999, when the command runs, then the error isno implementation 999and does not nameretired-tests-check(today it does).worktree_rootis W and a Boot root R, with the retired test, required-work text or unchanged file present only under W, whenretired-tests-check,required-work-checkorunchanged-checkruns from R, then each judges W (today they judge R).Implementation[@id]in a method other thaninitialize, including a lazy@x ||= Implementation[@id], when the cop runs, then it reports an offense (today it reports none). In the same test,Implementation[id]insideinitialize,ENV["X"],Thread.current[:k], a constant-hash lookup andSequel[:t]report no offense, andplugins/os-sdlc/lib/os_sdlc/reports no offense with noExcludeentry and no path list added for the cop.Owner sketch
Origin
.cc-os/os/handoffs/2026-10-27-1542-lint-cops-track.md); re-ruled 2026-10-28 (see Ruling).VERDICT: CREATE
Waiting on Jared's ruling (question at the top of the body) and on #1381.
#1381 landed (main
e2588c72): Db is matched by constant path, Db.method() is flagged, and the dead /controller//* glob is replaced by the Boot entry layer (boot.rb, boot/). Cop: 0 hits over plugins/os-sdlc before and after. #1381's test-glob shape carve-out is deferred to this ticket's ruling: the classes under tests/ that reach the registry (RollbackPerTest, SubagentStopScenario, SubagentStartScenario, CliNextInstructionHelpers, RepeatedNextRowCheck, DispatchTestCase, UnboundDbTestCase) are helpers called by entry points, the same question as Acceptance/Placing/Accounting/Abandoning. One ruling settles both.Ruling analysis, 2026-10-28 (session f258fae3). The ruling is still open: Jared decides. This comment replaces the A/B memo in the 2026-10-28 handoff.
Evidence: the lookup runs twice, and the message is wrong
mutation-check 999andunchanged-check 999both printretired-tests-check no implementation 999. The patternModel[id] || raise(...)is copied into 7 places: Acceptance, AcceptanceCheck, Placing, ReportLookup, Abandon, RerouteIntake, and Accounting.Root: the row already knows it
implementation/targeting.rb:def work_root(fallback) = self[:worktree_root] || fallback. The four checks use root in different ways:implementation.project(root), so the row's worktree_root comes firstGate::UnchangedCheck.for(root), the root that boot passesPossible bug: three checks can look in the cwd and not in the run's worktree. Nobody has confirmed that this happens in practice.
Roles (Jared's restaurant analogy, mapped to patterns)
Inputsreads the request,Commands::TABLEroutes it)Implementation.fetch!(id, command), a new method and the only place that does the lookupMediator does not fit here. A Mediator coordinates peer objects that talk to each other, and the cooks here do not talk to each other.
Revised ruling 1 (recommended, 80%)
Implementation.fetch!(id, command)finds the row or raises with the command name. The cop flagsModel[id] || raiseeverywhere except that method. This rule is simpler than a list of allowed classes. A specialist may ask the pantry. The defect is a copy of the pantry lookup.Model[id].fetch!once and gives the row, plusimplementation.work_root(fallback), to its cooks.Open choice: how does the cop recognize an expediter, if it must? (a) Boot::Commands::TABLE refers to the class. (b) A role name or a place in the tree. A named role is easier for agents to find, so (b) is now the better choice. Before you name it, check the CRC job categories. #834 added "entry controllers", and that may already be this role.
#1381 test globs: that is a separate ruling. Tests build rows in setup, which is a different question.
Work started 2026-10-28 via /os-sdlc:implement on branch ticket-1450 (session 6ee3ad76), on the body rewritten to Jared's 2026-10-28 ruling (lookup in initialize; AcceptanceCheck absorbed into Acceptance).
Resolution
Done: Ruled shape shipped: Implementation::Acceptance.new(id, root, io) loads the row once in initialize, raises its own 'no implementation ', and judges implementation.work_root(root); AcceptanceCheck absorbed and deleted; Implementation#ticket_shape; Placing, ReportLookup, Abandon, RerouteIntake look up in initialize; RegistryOutsideEntryPoint flags Model[] outside initialize by shape only; lesson and analysis updated. Merged
5ccc305f.Evidence: Implementation 213 escalated (95) on a test defect; hand-finished per the escalation procedure: full suite 2900 runs 0 failures; rubocop 14 changed files 0 offenses; RegistryOutsideEntryPoint over lib 938 files 0 offenses; one opus design audit, must-fix items 1,3,4,5,6 applied. Run 213 abandoned with reason.
Follow-ups: Dropped: route the other 6 TicketShape.new callers through Implementation#ticket_shape (ticket-skeptic DROP: depth-1 rule, Need-to-exist test failed; no failure, convenience only). Dropped: abandon.rb private helpers still take the row as a parameter (style only).