os-sdlc: crc-census, crc-records, crc-tickets, crc-score, crc-placement, crc-file enter through Thor #1677

Closed
opened 2026-11-10 19:47:03 +00:00 by jared · 2 comments
Owner

Problem

Six crc-* commands still enter through OsSdlc::Boot.run: crc-census, crc-records, crc-tickets, crc-score, crc-placement, and crc-file. Boot parses their argv by hand in Boot::Inputs, binds the pipeline database, and calls Run.new(io, project, request).call. None of the six reaches a Sequel model, so the database bind is unnecessary work and it creates .sdlc/pipeline.db. Each Run takes the full io object but reads only io.stdout and io.stderr; Crc::Census::Run also reads io.git, and Crc::Filing::Run also reads io.cwd and io.backlog_process. The other five crc-* commands moved to Thor in #1675 and #1676. 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 a Project, that project.repo is lazy, and that a Run object returns a result that the command prints. Rulings 121 and 122 (docs/decisions/rulings.tsv) set the shape: one Thor class per concept over the base Crc::CLI, and the help that Thor generates.

Approach

The language is Ruby. The suite command is ruby test/all.rb, run from plugins/os-sdlc. Read these files first; they are the pattern from #1676: lib/os_sdlc/crc/cli.rb, lib/os_sdlc/crc/cli/invocation.rb, lib/os_sdlc/crc/cli/shell.rb, lib/os_sdlc/crc/command_result.rb, lib/os_sdlc/crc/shares/cli.rb, lib/os_sdlc/crc/shares/run.rb, bin/crc-shares, and the tests test/crc/shares_cli_test.rb, test/crc/thor_entries_test.rb, test/support/crc_thor_scenario.rb.

  • One Thor class per concept inherits from Crc::CLI: Crc::Census::CLI, Crc::Records::CLI, Crc::Tickets::CLI, Crc::Score::CLI, Crc::Placement::CLI, and Crc::Filing::CLI (for crc-file), each at lib/os_sdlc/crc/<concept>/cli.rb. Each declares one command with its arguments and options through Thor (desc, method_option). The command takes no raw argv and calls no Boot::Inputs, Boot::Inputs::CrcArgv, or Boot::ArgvOption parser. Do not move option declarations into a module to pass the class-length cop.
  • Each bin/ script calls its own class with the command name first, as bin/crc-shares does: for example exit OsSdlc::Crc::Census::CLI.start(["census", *ARGV], process: OsSdlc::ProcessIO.from_process).
  • Arguments and options, as today. Every command has --target NAME (required).
    • census RUN_DIR [PATH...] with --loader FILE, --exclude PREFIX (repeatable), and --since SHA.
    • records RUN_DIR with --write (boolean).
    • tickets RUN_DIR with --session ID (required), --transcript PATH (required), --out DIR, --scope-label, --priority, --suite-command, and --eager-load-command.
    • score RUN_DIR.
    • placement with no positional argument; a positional argument is refused with exit 2. The run directory stays docs/crc/<target>.
    • file TICKETS_JSON with --os-backlog CMD (required) and --order KEYS (required, a comma list).
  • Each Run takes the project and its request, and no io. Run#call returns a result and writes to no stream. The command prints what the result holds and returns the status. crc-score and crc-placement print TSV text, not JSON, and crc-file can print a JSON array to stdout and problem lines to stderr in the same run; widen the shared result value Crc::CommandResult so that it carries the text for stdout and the lines for stderr for all eleven commands, and keep every byte of today's stdout (including the absence of a final newline where a command uses print today).
  • Crc::Census::Run reads the repository from project.repo.
  • Crc::Filing::Run still needs the backlog client. The Thor command builds it from the working directory and the backlog process that process holds, as Crc::Filing::Run builds it today, and passes it in. Crc::Filing::Result#report(out:, err:) no longer takes streams.
  • The check "is there a ledger.tsv in the run directory?" gets one owner. Today Crc::CommandResult.for_ledger holds it inside the value and runs the whole command through yield, and Crc::Sweep::Run, Crc::Records::Run, and Crc::Tickets::Run each hold their own copy with the same error text. Move the check to one small object (for example Crc::LedgerGate.new(command, path).call { ... }, which returns the failed result <command>: no ledger.tsv in <run> or the reported result of the block), use it in every run object that has the check, and remove for_ledger from Crc::CommandResult so that the value only carries its fields (lens heuristic A18; the contract audit of #1676 flagged this and a challenge confirmed a code defect).
  • No command of the six connects the database.
  • The six entries leave Boot::Commands::TABLE. Every crc_* method leaves Boot::Inputs (including crc_run_dir_input). The CrcArgv methods and UsageText entries that only these six read go. CrcArgv keeps what detector, fast-gate, and collaborator-pass read.

Test-writer work. These existing tests pin behavior that this ticket changes, and no other node may edit tests. The test-writer changes them in the red round, with the new tests:

  • test/support/in_process_dispatch_runner.rb: add the six script names to the lookup THOR_ENTRIES. The lookup must not name a constant that does not exist yet at load time (resolve the class when the command runs), or every test in the suite fails to load before the production code exists.
  • The bad-argument tests that assert usage: crc-<name> (test/crc/census_bin_test.rb, filing_bin_test.rb, placement_bin_test.rb, records_bin_test.rb, tickets_bin_test.rb): assert exit 2, empty stdout, and non-empty stderr; rename a test whose name says "usage".
  • The smoke tests of the six commands (test/crc/*_bin_smoke_test.rb): use assert_thor_help from test/support/crc_help_assertions.rb (script name, each positional name, each option name, exit 0). Delete the old help helpers from that support file when no test uses them.
  • test/detector/entry_points_test.rb (maps bin/crc-census to "OsSdlc::Boot") and test/system_model_entry_journeys_test.rb (calls Boot.run("crc-census", ...) directly): change each to the new entry class, or to a command that stays on Boot when the test is about Boot and not about census. Read the test to see which.
  • test/crc_boot_entry_test.rb and test/crc/placement_rulings_boot_test.rb: they reach the commands through InProcessDispatchRunner; keep them and correct only an assertion that pins usage text.

Kept behavior. The existing suite pins these items. The change must keep each one.

  • Every existing test of the six commands passes with the same stdout bytes, the same stderr lines from the run objects (crc-census: no loader file X under R, crc-records: no ledger.tsv in <run>, crc-score: missing <file>, crc-file: <problem>, and the like), the same files written (ledger.tsv, vocabulary.tsv, batch-*.txt, wave-progress.json, ticket bodies under --out), and the same exit codes 0, 1, 2, and 3. For a bad argument the exit code stays 2 and stdout stays empty; the stderr text becomes the error text of Thor or a one-line error from code.
  • ADR-0232 stays in force: script names, flags, stdout shapes, and exit codes do not change. An unknown option is now refused with exit 2.
  • skills/crc-refactor/SKILL.md runs bin/crc-score --target P DIR > score.tsv: stdout of crc-score holds the TSV only.
  • No test starts a real process (.claude/rules/ruby-external-calls.md). ProcessStartGuard enforces this.
  • Every command that is not a crc-* command stays on Boot.run in this ticket.
  • No code comments, no cop Exclude list, no inline rubocop:disable.

Required work:

  1. present Crc::Census::CLI.start in plugins/os-sdlc/bin/crc-census
  2. present Crc::Records::CLI.start in plugins/os-sdlc/bin/crc-records
  3. present Crc::Tickets::CLI.start in plugins/os-sdlc/bin/crc-tickets
  4. present Crc::Score::CLI.start in plugins/os-sdlc/bin/crc-score
  5. present Crc::Placement::CLI.start in plugins/os-sdlc/bin/crc-placement
  6. present Crc::Filing::CLI.start in plugins/os-sdlc/bin/crc-file
  7. absent Boot.run in plugins/os-sdlc/bin/crc-census
  8. absent Boot.run in plugins/os-sdlc/bin/crc-records
  9. absent Boot.run in plugins/os-sdlc/bin/crc-tickets
  10. absent Boot.run in plugins/os-sdlc/bin/crc-score
  11. absent Boot.run in plugins/os-sdlc/bin/crc-placement
  12. absent Boot.run in plugins/os-sdlc/bin/crc-file
  13. absent "crc- in plugins/os-sdlc/lib/os_sdlc/boot/commands.rb
  14. absent crc_run_dir_input in plugins/os-sdlc/lib/os_sdlc/boot/inputs.rb
  15. absent crc_census in plugins/os-sdlc/lib/os_sdlc/boot/inputs.rb
  16. absent crc_tickets in plugins/os-sdlc/lib/os_sdlc/boot/inputs.rb
  17. absent usage: crc- in plugins/os-sdlc/lib/os_sdlc/boot/inputs/usage_text.rb
  18. absent ticket_options in plugins/os-sdlc/lib/os_sdlc/boot/inputs/crc_argv.rb
  19. absent for_ledger in plugins/os-sdlc/lib/os_sdlc/crc/command_result.rb

Acceptance criteria

  1. OsSdlc::Crc::Census::CLI.start(["census", "--target", target, run_dir], process: process) reads the tracked files through the repository that process.git supplies to the project, prints the census report as pretty JSON to the stdout of process, and returns the status of the report.
  2. OsSdlc::Crc::Records::CLI.start(["records", "--target", target, run_dir], process: process) prints the records report as pretty JSON to the stdout of process and returns its status.
  3. OsSdlc::Crc::Tickets::CLI.start(["tickets", "--target", target, "--session", id, "--transcript", path, run_dir], process: process) prints the tickets report as pretty JSON to the stdout of process and returns its status.
  4. OsSdlc::Crc::Score::CLI.start(["score", "--target", target, run_dir], process: process) prints the score lines (file<TAB>score<TAB>detectors) to the stdout of process, with the same bytes as crc-score prints today, and returns 0.
  5. OsSdlc::Crc::Placement::CLI.start(["placement", "--target", target], process: process) prints the placement TSV to the stdout of process, with the same bytes as crc-placement prints today, and returns its status.
  6. OsSdlc::Crc::Filing::CLI.start(["file", "--target", target, "--os-backlog", cmd, "--order", "a,b", tickets_json], process: process) files through the backlog process that process holds, prints the JSON array of filed tickets to the stdout of process, and returns 0 when every ticket is filed and confirmed.
  7. Run.new(project, request).call returns a result whose status is an Integer and takes no stream argument, for each of Crc::Census::Run (with a fake repository assigned to project.repo), Crc::Records::Run, Crc::Tickets::Run, Crc::Score::Run, Crc::Placement::Run, and Crc::Filing::Run.
  8. With a missing detector TSV, Crc::Score::Run.new(project, request).call returns a result with status 2, nothing for stdout, and the error line crc-score: missing <file>.
  9. None of the six commands calls the database bind: a test that starts each class in a project directory finishes with no call to Db.connect_project! and with Sequel::Model.db unchanged.
  10. For each of the six classes, start with --help after the command name returns 0, prints help text to the stdout of process that names the script (crc-census for Crc::Census::CLI), each positional argument, and each option, and prints nothing to the global $stdout.
  11. For each of the six classes, start with no --target returns 2, prints nothing to the stdout of process, and prints a line that names --target to the stderr of process.
  12. Crc::Tickets::CLI.start with no --session returns 2 with empty stdout and a stderr line that names --session; with no --transcript it returns 2 with a stderr line that names --transcript.
  13. Crc::Placement::CLI.start(["placement", "--target", target, "extra"], process: process) returns 2 with empty stdout and a non-empty stderr line.
  14. Crc::Filing::CLI.start with no --os-backlog returns 2 with a stderr line that names --os-backlog; with no --order it returns 2 with a stderr line that names --order.
  15. Crc::Census::CLI.start(["census", "--target", target, "--exclude", "a", "--exclude", "b", "--since", sha, "--loader", file, run_dir, "p1", "p2"], process: process) gives Crc::Census::Run the excludes a and b, the since commit, the loader file, and the paths p1 and p2, shown through the result of the run.
  16. Crc::Records::CLI.start with --write rewrites the ledger, and without --write it leaves the ledger unchanged.
  17. Boot::Commands::TABLE holds no key that starts with crc-.
  18. With no ledger.tsv in the run directory, the one ledger check object returns a result with status 2, nothing for stdout, and the error line crc-records: no ledger.tsv in <run> for the command name crc-records, and it does not run its block; with a ledger.tsv it returns the reported result of its block.

Owner sketch

Each Crc::<Concept>::CLI (Thor) owns the command name, the arguments, and the options of its one command; it builds the request, hands the run class to Crc::CLI::Invocation, and returns the status. Crc::CLI and Crc::CLI::Invocation keep what every crc-* entry shares. Each Crc::<Concept>::Run owns the work for one request and returns the result; it writes nothing. Crc::CommandResult owns the exit status, the text for stdout, and the lines for stderr of one run, for all eleven commands. OsSdlc::Project owns its repository: Crc::Census::Run reads project.repo. Crc::Filing::CLI owns the build of the backlog client from process, until a later ticket moves adapters to the project.

Out of scope

Commands that are not crc-* commands, project.connect(:db), the removal of Boot, the removal of git and backlog_process from ProcessIO, a new name for Boot::Inputs::CrcArgv, and cop changes.

Reproduce

n/a: feature ticket.

Origin

Trigger: n/a: design ticket named by the user, not a run that showed a fault
Improvised this session: none
Chain: six crc-* commands bind a database they never use and pass an io they mostly ignore ← every command enters through Boot.run and one command table ← ADR-0250 (retired habit) put all entries behind Boot
Session: 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/lib/os_sdlc/boot/commands.rb (Boot::Commands::TABLE), plugins/os-sdlc/lib/os_sdlc/crc/census/run.rb (OsSdlc::Crc::Census::Run), plugins/os-sdlc/lib/os_sdlc/crc/filing/run.rb (OsSdlc::Crc::Filing::Run)
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."). Shape per rulings 121 to 124 (predicted and orchestrator).

VERDICT: CREATE
REASON: The user named and approved it, and no open ticket covers moving these six crc commands to Thor. #1646 is the broad decision ticket that this slice implements.

## Problem Six `crc-*` commands still enter through `OsSdlc::Boot.run`: `crc-census`, `crc-records`, `crc-tickets`, `crc-score`, `crc-placement`, and `crc-file`. `Boot` parses their argv by hand in `Boot::Inputs`, binds the pipeline database, and calls `Run.new(io, project, request).call`. None of the six reaches a Sequel model, so the database bind is unnecessary work and it creates `.sdlc/pipeline.db`. Each `Run` takes the full `io` object but reads only `io.stdout` and `io.stderr`; `Crc::Census::Run` also reads `io.git`, and `Crc::Filing::Run` also reads `io.cwd` and `io.backlog_process`. The other five `crc-*` commands moved to Thor in #1675 and #1676. 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 a `Project`, that `project.repo` is lazy, and that a `Run` object returns a result that the command prints. Rulings 121 and 122 (`docs/decisions/rulings.tsv`) set the shape: one Thor class per concept over the base `Crc::CLI`, and the help that Thor generates. ## Approach The language is Ruby. The suite command is `ruby test/all.rb`, run from `plugins/os-sdlc`. Read these files first; they are the pattern from #1676: `lib/os_sdlc/crc/cli.rb`, `lib/os_sdlc/crc/cli/invocation.rb`, `lib/os_sdlc/crc/cli/shell.rb`, `lib/os_sdlc/crc/command_result.rb`, `lib/os_sdlc/crc/shares/cli.rb`, `lib/os_sdlc/crc/shares/run.rb`, `bin/crc-shares`, and the tests `test/crc/shares_cli_test.rb`, `test/crc/thor_entries_test.rb`, `test/support/crc_thor_scenario.rb`. - One Thor class per concept inherits from `Crc::CLI`: `Crc::Census::CLI`, `Crc::Records::CLI`, `Crc::Tickets::CLI`, `Crc::Score::CLI`, `Crc::Placement::CLI`, and `Crc::Filing::CLI` (for `crc-file`), each at `lib/os_sdlc/crc/<concept>/cli.rb`. Each declares one command with its arguments and options through Thor (`desc`, `method_option`). The command takes no raw argv and calls no `Boot::Inputs`, `Boot::Inputs::CrcArgv`, or `Boot::ArgvOption` parser. Do not move option declarations into a module to pass the class-length cop. - Each `bin/` script calls its own class with the command name first, as `bin/crc-shares` does: for example `exit OsSdlc::Crc::Census::CLI.start(["census", *ARGV], process: OsSdlc::ProcessIO.from_process)`. - Arguments and options, as today. Every command has `--target NAME` (required). - `census RUN_DIR [PATH...]` with `--loader FILE`, `--exclude PREFIX` (repeatable), and `--since SHA`. - `records RUN_DIR` with `--write` (boolean). - `tickets RUN_DIR` with `--session ID` (required), `--transcript PATH` (required), `--out DIR`, `--scope-label`, `--priority`, `--suite-command`, and `--eager-load-command`. - `score RUN_DIR`. - `placement` with no positional argument; a positional argument is refused with exit 2. The run directory stays `docs/crc/<target>`. - `file TICKETS_JSON` with `--os-backlog CMD` (required) and `--order KEYS` (required, a comma list). - Each `Run` takes the project and its request, and no `io`. `Run#call` returns a result and writes to no stream. The command prints what the result holds and returns the status. `crc-score` and `crc-placement` print TSV text, not JSON, and `crc-file` can print a JSON array to stdout and problem lines to stderr in the same run; widen the shared result value `Crc::CommandResult` so that it carries the text for stdout and the lines for stderr for all eleven commands, and keep every byte of today's stdout (including the absence of a final newline where a command uses `print` today). - `Crc::Census::Run` reads the repository from `project.repo`. - `Crc::Filing::Run` still needs the backlog client. The Thor command builds it from the working directory and the backlog process that `process` holds, as `Crc::Filing::Run` builds it today, and passes it in. `Crc::Filing::Result#report(out:, err:)` no longer takes streams. - The check "is there a `ledger.tsv` in the run directory?" gets one owner. Today `Crc::CommandResult.for_ledger` holds it inside the value and runs the whole command through `yield`, and `Crc::Sweep::Run`, `Crc::Records::Run`, and `Crc::Tickets::Run` each hold their own copy with the same error text. Move the check to one small object (for example `Crc::LedgerGate.new(command, path).call { ... }`, which returns the failed result `<command>: no ledger.tsv in <run>` or the reported result of the block), use it in every run object that has the check, and remove `for_ledger` from `Crc::CommandResult` so that the value only carries its fields (lens heuristic A18; the contract audit of #1676 flagged this and a challenge confirmed a code defect). - No command of the six connects the database. - The six entries leave `Boot::Commands::TABLE`. Every `crc_*` method leaves `Boot::Inputs` (including `crc_run_dir_input`). The `CrcArgv` methods and `UsageText` entries that only these six read go. `CrcArgv` keeps what `detector`, `fast-gate`, and `collaborator-pass` read. Test-writer work. These existing tests pin behavior that this ticket changes, and no other node may edit tests. The test-writer changes them in the red round, with the new tests: - `test/support/in_process_dispatch_runner.rb`: add the six script names to the lookup `THOR_ENTRIES`. The lookup must not name a constant that does not exist yet at load time (resolve the class when the command runs), or every test in the suite fails to load before the production code exists. - The bad-argument tests that assert `usage: crc-<name>` (`test/crc/census_bin_test.rb`, `filing_bin_test.rb`, `placement_bin_test.rb`, `records_bin_test.rb`, `tickets_bin_test.rb`): assert exit 2, empty stdout, and non-empty stderr; rename a test whose name says "usage". - The smoke tests of the six commands (`test/crc/*_bin_smoke_test.rb`): use `assert_thor_help` from `test/support/crc_help_assertions.rb` (script name, each positional name, each option name, exit 0). Delete the old help helpers from that support file when no test uses them. - `test/detector/entry_points_test.rb` (maps `bin/crc-census` to `"OsSdlc::Boot"`) and `test/system_model_entry_journeys_test.rb` (calls `Boot.run("crc-census", ...)` directly): change each to the new entry class, or to a command that stays on `Boot` when the test is about `Boot` and not about census. Read the test to see which. - `test/crc_boot_entry_test.rb` and `test/crc/placement_rulings_boot_test.rb`: they reach the commands through `InProcessDispatchRunner`; keep them and correct only an assertion that pins usage text. Kept behavior. The existing suite pins these items. The change must keep each one. - Every existing test of the six commands passes with the same stdout bytes, the same stderr lines from the run objects (`crc-census: no loader file X under R`, `crc-records: no ledger.tsv in <run>`, `crc-score: missing <file>`, `crc-file: <problem>`, and the like), the same files written (`ledger.tsv`, `vocabulary.tsv`, `batch-*.txt`, `wave-progress.json`, ticket bodies under `--out`), and the same exit codes 0, 1, 2, and 3. For a bad argument the exit code stays 2 and stdout stays empty; the stderr text becomes the error text of Thor or a one-line error from code. - ADR-0232 stays in force: script names, flags, stdout shapes, and exit codes do not change. An unknown option is now refused with exit 2. - `skills/crc-refactor/SKILL.md` runs `bin/crc-score --target P DIR > score.tsv`: stdout of `crc-score` holds the TSV only. - No test starts a real process (`.claude/rules/ruby-external-calls.md`). `ProcessStartGuard` enforces this. - Every command that is not a `crc-*` command stays on `Boot.run` in this ticket. - No code comments, no cop `Exclude` list, no inline `rubocop:disable`. Required work: 1. present `Crc::Census::CLI.start` in `plugins/os-sdlc/bin/crc-census` 2. present `Crc::Records::CLI.start` in `plugins/os-sdlc/bin/crc-records` 3. present `Crc::Tickets::CLI.start` in `plugins/os-sdlc/bin/crc-tickets` 4. present `Crc::Score::CLI.start` in `plugins/os-sdlc/bin/crc-score` 5. present `Crc::Placement::CLI.start` in `plugins/os-sdlc/bin/crc-placement` 6. present `Crc::Filing::CLI.start` in `plugins/os-sdlc/bin/crc-file` 7. absent `Boot.run` in `plugins/os-sdlc/bin/crc-census` 8. absent `Boot.run` in `plugins/os-sdlc/bin/crc-records` 9. absent `Boot.run` in `plugins/os-sdlc/bin/crc-tickets` 10. absent `Boot.run` in `plugins/os-sdlc/bin/crc-score` 11. absent `Boot.run` in `plugins/os-sdlc/bin/crc-placement` 12. absent `Boot.run` in `plugins/os-sdlc/bin/crc-file` 13. absent `"crc-` in `plugins/os-sdlc/lib/os_sdlc/boot/commands.rb` 14. absent `crc_run_dir_input` in `plugins/os-sdlc/lib/os_sdlc/boot/inputs.rb` 15. absent `crc_census` in `plugins/os-sdlc/lib/os_sdlc/boot/inputs.rb` 16. absent `crc_tickets` in `plugins/os-sdlc/lib/os_sdlc/boot/inputs.rb` 17. absent `usage: crc-` in `plugins/os-sdlc/lib/os_sdlc/boot/inputs/usage_text.rb` 18. absent `ticket_options` in `plugins/os-sdlc/lib/os_sdlc/boot/inputs/crc_argv.rb` 19. absent `for_ledger` in `plugins/os-sdlc/lib/os_sdlc/crc/command_result.rb` ## Acceptance criteria 1. `OsSdlc::Crc::Census::CLI.start(["census", "--target", target, run_dir], process: process)` reads the tracked files through the repository that `process.git` supplies to the project, prints the census report as pretty JSON to the stdout of `process`, and returns the status of the report. 2. `OsSdlc::Crc::Records::CLI.start(["records", "--target", target, run_dir], process: process)` prints the records report as pretty JSON to the stdout of `process` and returns its status. 3. `OsSdlc::Crc::Tickets::CLI.start(["tickets", "--target", target, "--session", id, "--transcript", path, run_dir], process: process)` prints the tickets report as pretty JSON to the stdout of `process` and returns its status. 4. `OsSdlc::Crc::Score::CLI.start(["score", "--target", target, run_dir], process: process)` prints the score lines (`file<TAB>score<TAB>detectors`) to the stdout of `process`, with the same bytes as `crc-score` prints today, and returns 0. 5. `OsSdlc::Crc::Placement::CLI.start(["placement", "--target", target], process: process)` prints the placement TSV to the stdout of `process`, with the same bytes as `crc-placement` prints today, and returns its status. 6. `OsSdlc::Crc::Filing::CLI.start(["file", "--target", target, "--os-backlog", cmd, "--order", "a,b", tickets_json], process: process)` files through the backlog process that `process` holds, prints the JSON array of filed tickets to the stdout of `process`, and returns 0 when every ticket is filed and confirmed. 7. `Run.new(project, request).call` returns a result whose `status` is an Integer and takes no stream argument, for each of `Crc::Census::Run` (with a fake repository assigned to `project.repo`), `Crc::Records::Run`, `Crc::Tickets::Run`, `Crc::Score::Run`, `Crc::Placement::Run`, and `Crc::Filing::Run`. 8. With a missing detector TSV, `Crc::Score::Run.new(project, request).call` returns a result with `status` 2, nothing for stdout, and the error line `crc-score: missing <file>`. 9. None of the six commands calls the database bind: a test that starts each class in a project directory finishes with no call to `Db.connect_project!` and with `Sequel::Model.db` unchanged. 10. For each of the six classes, `start` with `--help` after the command name returns 0, prints help text to the stdout of `process` that names the script (`crc-census` for `Crc::Census::CLI`), each positional argument, and each option, and prints nothing to the global `$stdout`. 11. For each of the six classes, `start` with no `--target` returns 2, prints nothing to the stdout of `process`, and prints a line that names `--target` to the stderr of `process`. 12. `Crc::Tickets::CLI.start` with no `--session` returns 2 with empty stdout and a stderr line that names `--session`; with no `--transcript` it returns 2 with a stderr line that names `--transcript`. 13. `Crc::Placement::CLI.start(["placement", "--target", target, "extra"], process: process)` returns 2 with empty stdout and a non-empty stderr line. 14. `Crc::Filing::CLI.start` with no `--os-backlog` returns 2 with a stderr line that names `--os-backlog`; with no `--order` it returns 2 with a stderr line that names `--order`. 15. `Crc::Census::CLI.start(["census", "--target", target, "--exclude", "a", "--exclude", "b", "--since", sha, "--loader", file, run_dir, "p1", "p2"], process: process)` gives `Crc::Census::Run` the excludes `a` and `b`, the since commit, the loader file, and the paths `p1` and `p2`, shown through the result of the run. 16. `Crc::Records::CLI.start` with `--write` rewrites the ledger, and without `--write` it leaves the ledger unchanged. 17. `Boot::Commands::TABLE` holds no key that starts with `crc-`. 18. With no `ledger.tsv` in the run directory, the one ledger check object returns a result with `status` 2, nothing for stdout, and the error line `crc-records: no ledger.tsv in <run>` for the command name `crc-records`, and it does not run its block; with a `ledger.tsv` it returns the reported result of its block. ## Owner sketch Each `Crc::<Concept>::CLI` (Thor) owns the command name, the arguments, and the options of its one command; it builds the request, hands the run class to `Crc::CLI::Invocation`, and returns the status. `Crc::CLI` and `Crc::CLI::Invocation` keep what every `crc-*` entry shares. Each `Crc::<Concept>::Run` owns the work for one request and returns the result; it writes nothing. `Crc::CommandResult` owns the exit status, the text for stdout, and the lines for stderr of one run, for all eleven commands. `OsSdlc::Project` owns its repository: `Crc::Census::Run` reads `project.repo`. `Crc::Filing::CLI` owns the build of the backlog client from `process`, until a later ticket moves adapters to the project. ## Out of scope Commands that are not `crc-*` commands, `project.connect(:db)`, the removal of `Boot`, the removal of `git` and `backlog_process` from `ProcessIO`, a new name for `Boot::Inputs::CrcArgv`, and cop changes. ## Reproduce n/a: feature ticket. ## Origin Trigger: n/a: design ticket named by the user, not a run that showed a fault Improvised this session: none Chain: six `crc-*` commands bind a database they never use and pass an `io` they mostly ignore ← every command enters through `Boot.run` and one command table ← ADR-0250 (retired habit) put all entries behind `Boot` Session: 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/lib/os_sdlc/boot/commands.rb (Boot::Commands::TABLE), plugins/os-sdlc/lib/os_sdlc/crc/census/run.rb (OsSdlc::Crc::Census::Run), plugins/os-sdlc/lib/os_sdlc/crc/filing/run.rb (OsSdlc::Crc::Filing::Run) 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."). Shape per rulings 121 to 124 (predicted and orchestrator). VERDICT: CREATE REASON: The user named and approved it, and no open ticket covers moving these six crc commands to Thor. #1646 is the broad decision ticket that this slice implements.
Author
Owner

Work started on branch ticket-1677 through /os-sdlc:implement.

Work started on branch ticket-1677 through /os-sdlc:implement.
Author
Owner

Resolution

Done: The six commands crc-census, crc-records, crc-tickets, crc-score, crc-placement and crc-file enter through per-concept Thor classes over Crc::CLI. Each Run takes (project, request), receives no stream, and returns Crc::CommandResult. Crc::LedgerGate is the one owner of the ledger check; CommandResult.for_ledger is gone. No crc-* entry remains in Boot.

Evidence: Implementation 275 complete at merge-ready (commit 08be0436b on branch ticket-1677, plus the follow-up test commit). Gates passed: programmer, behavior-verifier, suite, survivors, contract-auditor round 2. Full suite: 4026 runs, 0 failures.

Follow-ups: 1) Detector::EntryPoints::InputHops reads only Inputs.method(:name), so the real table (input(:name) form) gives no hops; goes into the Boot-removal ticket, where the detector must follow Thor entries. 2) Evidence-tool delay: recorded on #1678. 3) Runner-run suite and survivor gates with no agent: to be captured through /os-backlog:capture.

## Resolution **Done:** The six commands crc-census, crc-records, crc-tickets, crc-score, crc-placement and crc-file enter through per-concept Thor classes over Crc::CLI. Each Run takes (project, request), receives no stream, and returns Crc::CommandResult. Crc::LedgerGate is the one owner of the ledger check; CommandResult.for_ledger is gone. No crc-* entry remains in Boot. **Evidence:** Implementation 275 complete at merge-ready (commit 08be0436b on branch ticket-1677, plus the follow-up test commit). Gates passed: programmer, behavior-verifier, suite, survivors, contract-auditor round 2. Full suite: 4026 runs, 0 failures. **Follow-ups:** 1) Detector::EntryPoints::InputHops reads only Inputs.method(:name), so the real table (input(:name) form) gives no hops; goes into the Boot-removal ticket, where the detector must follow Thor entries. 2) Evidence-tool delay: recorded on #1678. 3) Runner-run suite and survivor gates with no agent: to be captured through /os-backlog:capture. <!-- os-backlog kind: resolution -->
jared closed this issue 2026-11-10 21:02:12 +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#1677
No description provided.