Skip to content

feat(scout): the 24/7 scout is an exit-3 stub in the Go binary while its LaunchAgent still runs it #372

Description

@linhdmn

Problem

The automatic queue writer is dead in the shipped binary. devagent scout (FR-SCOUT-01, "24/7 scout: research backlog -> PRD -> queue") returns the exit-3 notPortedError for every mode except --replay (internal/cli/actions_scout.go:25-27), while scripts/install-scout-launchagent.sh still installs a LaunchAgent whose ProgramArguments run devagent scout --repo … --interval … — so the 24/7 scout has been failing on every tick, and scout.heartbeat.json goes stale with nothing to explain it.

The deterministic core was ported long ago (BuildScoutPrompt, ExtractScoutPayload, ParseScoutOutput, FallbackTask, heartbeat, single-instance lock in internal/scout/scout.go); only the live cycle is missing — and its two stated blockers (the queue package, the worker runtime) landed since: internal/queue (FR-GO-04 #194) and internal/workers (FR-GO-05).

Consequence for the factory: internal/tracker/tracker.go:1-5 documents the intended triangle "scout writes the queue, builder consumes it", but with the scout leg dead the queue only ever receives rows a human enqueues through POST /dispatch, so the builder lane starves into LLM self-selection (see #355).

Requested change

Port runScoutOnce / runScoutLoop into internal/scout and wire the existing flags (--once, --interval, --worker, --timeout, --repo, --dry-run):

  1. lock → config (scout.{worker,model,maxQueued,intervalMinutes}) → queue-depth check before dispatch (dispatch is the paid step) → prompt → workers.GetWorker(name).Spawn → ExtractScoutPayload → ParseScoutOutput → queue.EnqueueTask (task-PRD sidecar included) → heartbeat → release lock.
  2. An empty ResultText with a nonzero exit code is an error, never a silent empty payload (the stale-binary class, loops 285-290); unparseable output falls back to the deterministic per-UTC-day FallbackTask id; ErrAlreadyQueued is a dedupe hit, not a failure.
  3. --dry-run runs the documented no-AI-call preview: every step except the enqueue/heartbeat writes, and no worker spawn.
  4. Remove the stale "live dispatch is NOT ported" claims from internal/scout/extract.go and scout.go package headers.

Acceptance

  • devagent scout --once enqueues exactly one row from a canned payload, writes .devagent/prds/<id>.md + scout.heartbeat.json, and a second identical cycle reports deduped with no new row.
  • A live foreign scout.lock holder ⇒ locked, zero writes.
  • Queue at scout.maxQueued ⇒ queue-full and no dispatch.
  • devagent scout --once --dry-run creates no files; --replay output stays byte-identical.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions