os-sdlc: crc-sweep enters through Thor; Project owns a lazy repo #1675
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#1675
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
bin/crc-sweeprunsOsSdlc::Boot.run("crc-sweep", OsSdlc::ProcessIO.from_process, ARGV).Bootlooks the name up inBoot::Commands::TABLE, parses argv by hand inBoot::Inputs.crc_sweep, binds the pipeline database, and then callsCrc::Sweep::Run.new(io, project, run).call.Crc::Sweep::Runreaches no Sequel model, so the database bind is unnecessary work and it creates.sdlc/pipeline.db.Crc::Sweep::Runtakes the fullioobject, but it uses onlyio.git,io.stdout, andio.stderr. The owner ruled on 2026-11-10 that Thor is the entry for os-sdlc commands, that a command and the classes below it take aProject, thatproject.repois lazy, and that aRunobject returns a result that the command prints. ADR-0123 no longer excludes os-sdlc dispatch commands from Thor. This ticket movescrc-sweepas the first full path. The other commands follow in later tickets.Approach
The language is Ruby. The suite command is
ruby test/all.rb, run fromplugins/os-sdlc.OsSdlc::Projectgains a lazyreporeader and arepo=writer.repois not an attribute: it never entersattributes, the snapshot, or equality. The reader builds the repository at the project root through the existingGitRepositoriesseam on first use, and memoizes it. A copy made bywithorbound_tokeeps an assigned repository.OsSdlc::Crc::CLI(lib/os_sdlc/crc/cli.rb) has one command,sweep RUN_DIR. Thor does the parsing: the command declaresRUN_DIRas its one argument and declares the options--target NAME(required),--write(boolean),--exclude PATH(repeatable), and--history PATH(repeatable). The command takes no raw argv and calls noBoot::InputsorBoot::ArgvOptionparser. Thor error text goes to the stderr thatprocessholds, not to the global$stderr. Follow the existing Thor classes for the form (lib/os_sdlc/evidence/collection.rb,lib/os_sdlc/boot/onboarding.rb): the entry passes collaborators through the Thor start config.bin/crc-sweepbecomesexit OsSdlc::Crc::CLI.start(["sweep", *ARGV], process: OsSdlc::ProcessIO.from_process).sweepcommand finds the project root from the working directory thatprocessholds, loads theProjectfor the target, and does not connect the database. WhileProcessIOstill holdsgit, the command gives the project its repository fromprocess.git, so the in-process tests that passgit: FakeGit...continue to work.Crc::Sweep::Runtakes the project and the run settings, and noio.Run#callreturns a result that holds the exit status, the report (or nil), and the error line (or nil). It writes to no stream. Thesweepcommand prints the report to stdout as JSON, prints the error line to stderr, and returns the status."crc-sweep"entry leavesBoot::Commands::TABLE, andBoot::Inputs.crc_sweepgoes. The shared test entry (test/support/in_process_bin.rband its runners) startsCrc::CLIfor the namecrc-sweep.Kept behavior. The existing suite pins these items, so they are not red-round criteria. The change must keep each one.
crc-sweeptest passes with the same stdout bytes, stderr lines, and exit codes (test/crc/sweep_bin_test.rb, the sweep tests intest/crc_boot_entry_test.rb). This includes exit 2 withcrc-sweep: git ls-files failed in <root>outside a git work tree, exit 3 with the loud line for an unexpected error, and exit 3 for a directory with no project. One thing changes: for a bad argument (no arguments, a missing--target, a missingRUN_DIR, an unknown option) the exit code stays 2 and stdout stays empty, but the stderr text becomes the error text of Thor. An existing test that pins the old usage text forcrc-sweep(test/crc/sweep_bin_test.rbtest_prints_usage_and_exits_two_without_arguments, and anycrc-sweepusage assertion intest/crc_boot_entry_test.rb) is updated to assert exit 2, empty stdout, and non-empty stderr.crc-sweep, its flags, and its output shapes do not change..claude/rules/ruby-external-calls.md).ProcessStartGuardenforces this.crc-*commands and every other command stay onBoot.runin this ticket.Required work:
Crc::CLI.startinplugins/os-sdlc/bin/crc-sweepBoot.runinplugins/os-sdlc/bin/crc-sweep"crc-sweep" =>inplugins/os-sdlc/lib/os_sdlc/boot/commands.rbdef self.crc_sweepinplugins/os-sdlc/lib/os_sdlc/boot/inputs.rbclass CLI < Thorinplugins/os-sdlc/lib/os_sdlc/crc/cli.rbBoot::Inputsinplugins/os-sdlc/lib/os_sdlc/crc/cli.rbBoot::ArgvOptioninplugins/os-sdlc/lib/os_sdlc/crc/cli.rbmethod_option :targetinplugins/os-sdlc/lib/os_sdlc/crc/cli.rbAcceptance criteria
Project.new(name: "p", root: root).reporeturns anOsGit::Repositorywhoserootis the project root as a String, and a second call returns the same object.project.repo = fake,project.reporeturnsfake, andproject.with(tracker: "t").reporeturns the samefake.==when only one of them has an assignedrepo, andproject.attributeshas no"repo"key.OsSdlc::Crc::CLI.start(["sweep", "--target", target, run_dir], process: process)with a ledger that has one live hit prints the sweep report as JSON to the stdout ofprocessand returns 1.Crc::Sweep::Run.new(project, run).call, with a fake repository assigned toproject.repo, returns a result whosestatusis 1 and whosereportholds the live count, and it takes no stream argument.sweepcommand does not call the database bind: a test that startsCrc::CLIin a project directory finishes with no call toDb.connect_project!and withSequel::Model.dbunchanged.Crc::CLI.start(["sweep", run_dir], process: process)with no--targetreturns 2, prints nothing to the stdout ofprocess, and prints a line that names--targetto the stderr ofprocess.Crc::CLI.start(["sweep", "--target", target], process: process)with noRUN_DIRreturns 2, prints nothing to the stdout ofprocess, and prints a non-empty error to the stderr ofprocess.Crc::CLI.start(["sweep", "--target", target, "--exclude", "a", "--exclude", "b", "--history", "h", "--write", run_dir], process: process)givesCrc::Sweep::Runthe settingsexcludes: ["a", "b"],histories: ["h"], andwrite: true, shown by the result of the run (the missing-input error line namesa,b, andhwhen those paths do not exist).Owner sketch
OsSdlc::Crc::CLI(Thor) owns the command names and option parsing for thecrc-*commands; in this ticket it hassweeponly. It finds the project, calls the run object, prints the result, and returns the status.OsSdlc::Crc::Sweep::Runowns the sweep for one request and returns the result; it writes nothing.OsSdlc::Projectowns its repository: other objects readproject.repo. The error-to-exit-code wrapper stays withProcessIO::Outcome(guarded), which the command calls.Out of scope
The other
crc-*commands,project.connect(:db), the removal ofBoot, the removal ofgitfromProcessIO, and cop changes. These are later tickets of the same change.Origin
Trigger: n/a: design ticket named by the user, not a run that showed a fault
Improvised this session: none
Chain:
crc-sweepbinds a database it never uses and takes anioit mostly ignores ← every command enters throughBoot.runand one command table ← ADR-0250 (retired habit) put all entries behindBootSession: a48658d5-b566-40ce-8257-ebeabf56f7e1
Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os-plugins-os-sdlc/a48658d5-b566-40ce-8257-ebeabf56f7e1.jsonl
Where: plugins/os-sdlc/bin/crc-sweep, plugins/os-sdlc/lib/os_sdlc/crc/sweep/run.rb (OsSdlc::Crc::Sweep::Run), plugins/os-sdlc/lib/os_sdlc/boot/commands.rb
Root candidate: #1646
Approved by: Jared named and approved this change in session on 2026-11-10 ("List the changes we are making. Then implement them.").
VERDICT: CREATE
REASON: #1646 is the open decision ticket for moving all os-sdlc entries from Boot to Thor. This ticket is the first concrete slice (crc-sweep only), and no open ticket covers its pieces.
Work started on branch ticket-1675 through /os-sdlc:implement (map poodr-ticket-implementation).
Resolution
Done: crc-sweep enters through the Thor class Crc::CLI. Thor declares RUN_DIR, --target (required), --write, --exclude and --history. Crc::Sweep::Run takes the Project, receives no streams and returns Crc::Sweep::Result; the Thor command prints. Project#repo is lazy. The command does not bind the pipeline database. The crc-sweep entry is gone from Boot::Commands::TABLE, Boot::Inputs and Boot::Inputs::UsageText.
Evidence: Implementation 271 ended at merge-ready (27 dispatches, one reroute after a ticket correction). Commit
317ebca74on branch ticket-1675. Suite gate, mutation survivor gate, behavior-verifier and the second contract audit passed.Follow-ups: None from this ticket. The remaining pieces of the plan that Jared ruled on 2026-11-10 (the other crc-* commands, the other commands without a model, the model commands with project.connect(:db), and the removal of Boot) each get a ticket through /os-backlog:capture.