Move the PostToolUse pending-dispatch lookup from the hook into the runner [capture] #576

Open
opened 2026-09-14 16:38:26 +00:00 by jared · 0 comments
Owner

Context

The new Sdlc/RequireInMethod cop (added 2026-09-14, branch require-hygiene) flags require "os_sdlc" inside load_runner_lib in plugins/os-sdlc/hooks/post_tool_use.rb. The hook does its cheap checks (Agent tool call, async_launched, pipeline DB file present) and then, still in-process, loads os_sdlc, connects to the pipeline DB, and calls models.dispatch.pending_dispatch_for before it pipes next to the runner. The hook's header comment names this lookup as a runner concern deferred from #550. A rubocop:disable Sdlc/RequireInMethod directive now marks the line.

Observed

The hook holds four methods that exist only to reach the pipeline DB: load_runner_lib, models_for, dispatch_lookup, pending_dispatch. That is a database query living in a hook that ADR-0172 defines as a pipe.

bundle exec rubocop --only Sdlc/RequireInMethod hooks
hooks/post_tool_use.rb:60:3: C: Sdlc/RequireInMethod: Move require "os_sdlc" to the top of the file. If only this method needs the library, the code that uses it belongs in its own adapter class.

Reproduce

n/a: design ticket. The directive suppresses the offense; the smell stays.

Expected

The hook keeps only the cheap checks and always pipes next when they pass. The runner performs the pending-dispatch lookup and answers empty when nothing is pending. The four methods and the directive leave the hook.

Illustration

# illustration, not spec
def handle_event(raw, event)
  agent, root = dispatchable_event(event)
  return unless dispatch_ready?(event, agent, root)

  OsSdlc::Hook::Pipe.call("next", event_name: "PostToolUse", io: StringIO.new(raw))
end

Cost to weigh: one runner boot per async Agent launch in a session that has a pipeline DB, even when no dispatch is pending. Measure hook wall time before and after; the pre-existing dispatch_ready? gate already excludes every project without a pipeline DB.

Origin

  • Trigger: bundle exec rubocop --only Sdlc/RequireInMethod lib bin hooks on branch require-hygiene, 2026-09-14
  • Improvised this session: # rubocop:disable Sdlc/RequireInMethod on the require line with the reason
  • Chain: in-method require ← DB lookup performed in the hook ← DESIGN (post_tool_use.rb header comment, deferred from #550; ADR-0172 hooks as pipes)
  • Root candidate: this ticket
  • Where: plugins/os-sdlc/hooks/post_tool_use.rb, load_runner_lib / pending_dispatch
  • Session: 7b78eeee-1c7c-4c15-ba40-4e32f3f53479
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/7b78eeee-1c7c-4c15-ba40-4e32f3f53479.jsonl
  • Skeptic: VERDICT: CREATE — design deferral explicitly named in #550's own header comment as out-of-scope; scoped architectural move with ADR-0172 provenance.
## Context The new `Sdlc/RequireInMethod` cop (added 2026-09-14, branch require-hygiene) flags `require "os_sdlc"` inside `load_runner_lib` in `plugins/os-sdlc/hooks/post_tool_use.rb`. The hook does its cheap checks (Agent tool call, `async_launched`, pipeline DB file present) and then, still in-process, loads `os_sdlc`, connects to the pipeline DB, and calls `models.dispatch.pending_dispatch_for` before it pipes `next` to the runner. The hook's header comment names this lookup as a runner concern deferred from #550. A `rubocop:disable Sdlc/RequireInMethod` directive now marks the line. ## Observed The hook holds four methods that exist only to reach the pipeline DB: `load_runner_lib`, `models_for`, `dispatch_lookup`, `pending_dispatch`. That is a database query living in a hook that ADR-0172 defines as a pipe. ``` bundle exec rubocop --only Sdlc/RequireInMethod hooks ``` ``` hooks/post_tool_use.rb:60:3: C: Sdlc/RequireInMethod: Move require "os_sdlc" to the top of the file. If only this method needs the library, the code that uses it belongs in its own adapter class. ``` ## Reproduce n/a: design ticket. The directive suppresses the offense; the smell stays. ## Expected The hook keeps only the cheap checks and always pipes `next` when they pass. The runner performs the pending-dispatch lookup and answers empty when nothing is pending. The four methods and the directive leave the hook. ## Illustration ```ruby # illustration, not spec def handle_event(raw, event) agent, root = dispatchable_event(event) return unless dispatch_ready?(event, agent, root) OsSdlc::Hook::Pipe.call("next", event_name: "PostToolUse", io: StringIO.new(raw)) end ``` Cost to weigh: one runner boot per async Agent launch in a session that has a pipeline DB, even when no dispatch is pending. Measure hook wall time before and after; the pre-existing `dispatch_ready?` gate already excludes every project without a pipeline DB. ## Origin - Trigger: `bundle exec rubocop --only Sdlc/RequireInMethod lib bin hooks` on branch require-hygiene, 2026-09-14 - Improvised this session: `# rubocop:disable Sdlc/RequireInMethod` on the require line with the reason - Chain: in-method require ← DB lookup performed in the hook ← DESIGN (post_tool_use.rb header comment, deferred from #550; ADR-0172 hooks as pipes) - Root candidate: this ticket - Where: plugins/os-sdlc/hooks/post_tool_use.rb, `load_runner_lib` / `pending_dispatch` - Session: 7b78eeee-1c7c-4c15-ba40-4e32f3f53479 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/7b78eeee-1c7c-4c15-ba40-4e32f3f53479.jsonl - Skeptic: VERDICT: CREATE — design deferral explicitly named in #550's own header comment as out-of-scope; scoped architectural move with ADR-0172 provenance.
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#576
No description provided.