Apply ExitOutsideEntryPoint to os, os-backlog, and os-me #1411
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#1411
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
Ticket #1402 added the cop
Sdlc/Structural/ExitOutsideEntryPoint(plugins/os-sdlc/lib/os_sdlc/cops/structural/exit_outside_entry_point.rb).The cop reports
exit,exit!, andabort(bare,Kernel., orProcess.) outside an entry point.An entry point is a file with a
#!first line or a file in abindirectory.An
exit!lexically inside aforkblock is allowed.Only
plugins/os-sdlc/.rubocop.ymlenables the cop.Eleven non-entry exit calls remain in the plugins
os,os-backlog, andos-me.An exit call deep in a library hides the process boundary: a caller cannot reuse the code without the process ending.
Observed
The call sites on main
367156a8(2026-10-26), verified by grep:The earlier list named
plugins/os/lib/worktree/cli.rb:31; the second call is now at line 33.plugins/os-backlog/.rubocop.ymldoes not load the os-sdlc cops.The repo-root
.rubocop.ymlsets onlyAllCopsandStyle/Documentation, so it does not load the Sdlc cops.plugins/osandplugins/os-mehave no plugin.rubocop.yml.Reproduce
n/a: design and lint-config ticket; the offenses appear only after the cop is loaded.
Approach
Several sites are Thor CLI classes.
Thor
startreturns no status, so each CLI must return an Integer status from arunmethod, and the bin stub callsexit.The design question per plugin is how each Thor command reports its status to
run.Each plugin may choose its own answer; the observable exit status must not change.
Required work:
Sdlc/Structural/ExitOutsideEntryPointinplugins/os-backlog/.rubocop.yml— check: presentSdlc/Structural/ExitOutsideEntryPointinplugins/os-backlog/.rubocop.ymlplugins/os/.rubocop.ymlthat loads the Sdlc cops loader and enables the cop — check: presentSdlc/Structural/ExitOutsideEntryPointinplugins/os/.rubocop.ymlplugins/os-me/.rubocop.ymlthat loads the Sdlc cops loader and enables the cop — check: presentSdlc/Structural/ExitOutsideEntryPointinplugins/os-me/.rubocop.ymlplugins/os-backlog/lib; the bin stub callsexit Backlog::CLI.run(ARGV, ...)— check: absentexitinplugins/os-backlog/lib/backlog/cli_exit.rbplugins/os/lib/handoff/cli.rbandplugins/os/lib/worktree/cli.rb— check: absentexit(outcome.code)inplugins/os/lib/worktree/cli.rbplugins/os-me/lib/os_me/cli.rb— check: absentexit(inplugins/os-me/lib/os_me/cli.rbRejected: leave the cop in os-sdlc only — the other plugins keep the hidden process boundary.
Acceptance criteria
plugins/os-backlog/.rubocop.yml, when rubocop runs with it, then it reports noSdlc/Structural/ExitOutsideEntryPointoffense.plugins/os/.rubocop.yml, when rubocop runs with it, then it reports noSdlc/Structural/ExitOutsideEntryPointoffense.plugins/os-me/.rubocop.yml, when rubocop runs with it, then it reports noSdlc/Structural/ExitOutsideEntryPointoffense.os-backlogwith no arguments, when it runs, then it exits 1 as before.os-backlogand a failingissue-createthat exited 1 before, when it runs, then it still exits 1.plugins/os/bin/worktreeand a command whose outcome code is nonzero, when it runs, then the process exits with that code.os-me held-outfor a held-out session, when it runs, then it exits 0; for any other session it exits 1.Owner sketch
Non-goals
Changes to the cop itself; plugins other than
os,os-backlog, andos-me.Origin
os,os-backlog,os-melibraries ← the cop loads only inplugins/os-sdlc/.rubocop.yml← DESIGN (#1402, commit367156a8)VERDICT: CREATE
Work started (AFK unit, fast-batch route): the os plugin part. Three ExitOutsideEntryPoint hits: plugins/os/lib/handoff/cli.rb:41, plugins/os/lib/worktree/cli.rb:24,33. os-me (
2e871a2b) and os-backlog (1cee5a2f) parts are already on main.Not filed (skeptic DROP, depth-1): six ExitOutsideEntryPoint hits stay outside os/os-me/os-backlog (plugins/cc-architect/scripts/lib/validate_marketplaces.rb:127,139,141,143; docs/system-design-format/spike/extract.rb:4; docs/triage/2026-10-10-backfill-ticket-categories.rb:31); no plugin target lints them today.
Resolution
Done: os part merged as fast-batch-1411 (
cf9f6c04, mergea3097e8e): Handoff::CLI and Worktree::CLI raise Handoff::ExitStatus / Worktree::ExitStatus in place of exit; bin/handoff and bin/worktree rescue it and exit e.status. os-me part2e871a2band os-backlog part1cee5a2fwere already on main. Design model regenerated with 'system-model build plugins/os' (dcb1a591).Evidence: lint-worklist --target os: 0 ExitOutsideEntryPoint (was 3). ruby plugins/os/tests/all.rb: 65 runs, 180 assertions, 0 failures. After refresh-plugins: 'worktree help' and 'handoff help' answer; 'handoff complete /nonexistent' prints the error and exits 1; 'worktree finish no-such-branch' exits 3 as on main. Required-work items 1-3 (.rubocop.yml files naming the cop) not added: lint-worklist already applies the Sdlc cops to the os, os-me and os-backlog targets; adding a plugins/os/.rubocop.yml would change rubocop config resolution for the os target and needs a ruling.
Follow-ups: Ruling for Jared: keep or drop required-work items 1-3 (per-plugin .rubocop.yml naming the cop). Predicted row R1 (keep one ExitStatus per namespace, 74) in docs/crc/os/fast-batch-1411/rulings.tsv.
Note from the AFK orchestrator: #1411 was closed with required-work items 1-3 (a
.rubocop.ymlper plugin that names the cop) not done, because a newplugins/os/.rubocop.ymlchanges which rubocop config the os target uses and needs Jared's ruling. Reopen if those items still matter.Ruling 2026-10-28 (Jared): drop items 1-3. lint/worklist_rubocop_config.rb:10 makes plugins/os-sdlc/.rubocop.yml the first config link for every target, so Sdlc/Structural/ExitOutsideEntryPoint already runs on os, os-me and os-backlog (lint-worklist --target os: 0 offenses). Per-plugin files would change only a bare rubocop run inside a plugin, would need project.yaml lint_config entries to affect the worklist, and would then drop the root AllCops Exclude list. Target scoping is unchanged either way.