memorysaver/agentic-engineering-patterns

aep-watch

- Ingests bug trackers, errors, and telemetry, then dedupes them into backlog stories.

Ver código fuente
Documento original del Skill

Contenido del repositorio de origen con títulos, ejemplos, código, tablas, enlaces e imágenes preservados.

Watch

Self-feeding work discovery. /aep-watch is a continuous/scheduled monitor: it pulls from configured sources (bug trackers, error streams, telemetry), classifies each finding with the same classifier as `/aep-reflect`, dedupes against the backlog, and writes new bug/refinement stories into product-context.yaml — which then flow into /aep-dispatch (or autopilot picks them up), closing the loop so the system keeps finding work without a human running /aep-envision or /aep-reflect by hand.

sources → [ /aep-watch: pull → classify → dedupe → write stories ] → product-context.yaml
                                                                          │
                                                                          ▼
                                                          /aep-dispatch  (or /aep-autopilot)

/aep-reflect is the human-in-the-loop classifier you run after shipping; /aep-watch is its always-on sibling — same logic, no human prompting each finding — the thing that makes the loop continuous. It feeds the same `stories` section `/aep-dispatch` reads, so discovered work re-enters the /aep-envision → /aep-map → /aep-dispatch → … → /aep-wrap → /aep-reflect cycle.

Session: Main workspace only (like /aep-autopilot) — respects the orchestrator boundary. Driver: /loop <interval> (Claude Code) or codex exec cron/launchd (Codex). Input: Sources configured in topology.routing.watch. Output: New bug / refinement stories appended to the stories section of product-context.yaml (or surfaced as proposals for confirmation — see Config).


Orchestrator Boundary

/aep-watch runs from the main workspace only and is an orchestrator, not an executor: the orchestrator boundary stated in /aep-autopilot applies here unchanged. It reads only:

  • the configured sources (via their APIs/feeds — see Step 1),
  • product-context.yaml (to dedupe and to write stories).

If a finding needs code investigation, that happens inside a workspace agent after the story is dispatched.

bash
# Main workspace guard
pwd | grep -q '.feature-workspaces' && echo "ABORT: Run /aep-watch from main workspace only" && exit 1
[ -f product-context.yaml ] || echo "ABORT: Run /aep-envision and /aep-map first"

Any worker /aep-watch spawns (e.g. a cheap CHECK delegate to fetch + classify a batch) is a `native-bg-subagent` on Claude Code, gated by the standard Post-Spawn Liveness Probe per /aep-executor (scripts/spawn-liveness-probe.sh <name> <agent_id>). If the probe fails, tear the spawn down and retry once; if the retry also fails, run the fetch + classify inline in the watch session for this tick (degraded but still signals-only). The watch session itself does not read workspace code.


Config

Watch is driven entirely by topology.routing.watch in product-context.yaml. Each sources[] entry names a source type (its adapter and finding shape live in references/telemetry-ingestion.md, which is where the types are defined):

yaml
topology:
  routing:
    full_auto: false # master switch (see below)
    watch:
      sources: # source types + adapters: references/telemetry-ingestion.md
        - type: bug_tracker # github_issues | linear | jira | sentry | datadog | log_stream
          query: "is:open label:bug"
        - type: error_stream
          dsn: "<sentry/rollbar/...>"
        - type: telemetry
          metric: "error_rate"
          threshold: 0.02
        - type: dogfood_report # dogfood findings (local / post-deploy / standalone)
          glob: ".dev-workflow/dogfood-*.md" # default; see telemetry-ingestion.md adapter
        - type: distillation # layer distillations (proposal-only synthesis from /aep-wrap)
          glob: "lessons-learned/distillations/*.yaml" # default; see telemetry-ingestion.md adapter
      interval: 30m # poll cadence for the /loop or cron driver
      auto_create: false # write stories directly vs. surface proposals
      since: null # high-water mark — last ingested timestamp (watch maintains this)

Confirmation policy (conservative by default): auto-create a story only when full_auto: true (master switch) OR watch.auto_create: true (per-watch opt-in, narrower than the master switch). Otherwise surface a proposal: write the story object to a watch_proposals block under topology.routing.watch and print it; nothing enters the stories section until a human approves (via /aep-reflect or inline). Under full_auto: true, watch writes straight into stories and /aep-dispatch / /aep-autopilot pick them up on the next tick.


The Watch Loop

Each tick runs the same four-step body. Idempotent — re-running with no new source data produces no new stories (the dedupe + since high-water mark guarantee it).

⓪ PRECHECK  → verify the /aep-map telemetry binding is complete (coverage_check)
① PULL      → fetch new findings from each configured source (since high-water mark)
② CLASSIFY  → run each finding through the /aep-reflect Step 2 classifier
③ DEDUPE    → drop findings that already map to an existing story
④ WRITE     → create bug/refinement stories (or surface proposals)

Step 0: Precondition — verify the map binding

/aep-watch consumes telemetry sources, so first confirm /aep-map actually bound them, so a watch that covers nothing says so. Run coverage_check() (the helper in references/telemetry-ingestion.md §1.5) over the signals this watch needs: each topology.routing.watch.sources[] entry (and any metric/error_stream it relies on) must resolve to a wired topology.routing.telemetry_sources entry with a metric_map.

  • Covered → proceed to Step 1.
  • Not covered (sources empty, or a referenced metric has no metric_map) →

report the gap rather than the coverage. Surface: "telemetry binding incomplete for <missing> — run /aep-map (Telemetry Binding step) before /aep-watch can ingest it", skip the uncovered sources, and (if nothing is covered) stop the tick with that message. A missing binding blocks; it never silently no-ops.

Postcondition: every covered source resolves to a wired telemetry_sources entry with a metric_map (file-glob sources dogfood_report/distillation are self-describing and exempt), or the tick surfaced the incomplete-binding message.

Step 1: Pull from Sources

For each entry in watch.sources, pull findings created/updated since watch.since, reducing each to the finding record and using the per-source adapters in references/telemetry-ingestion.md (→ The /aep-watch finding record; Dogfood-report adapter; Distillation adapter), which define the finding shape. Advance watch.since to the newest last_seen only after the tick completes successfully (a failed tick re-pulls rather than dropping findings).

  • File-glob sources (`dogfood_report`, `distillation`) carry no per-item

timestamp, so watch.since does not advance for them; re-scanning the glob each tick is harmless because Step 3 dedupes on each adapter's stable external_id (dogfood:<report>:<hash> / distillation:<layer>:<hash>). Being self-describing, neither is gated by Step 0's coverage_check.

  • `distillation` extra rule: items mapped to process (skill_amendments)

are never auto-created as stories — they surface to a human as proposed amendments regardless of full_auto/auto_create.

Postcondition: each configured source yielded zero or more finding records; watch.since is unchanged until the tick completes successfully.

Step 2: Classify Each Finding

Classify every finding with the exact same classifier as `/aep-reflect` Step 2 (bug / refinement / discovery / opportunity shift / process) — apply /aep-reflect "Classify Each Observation" (the canonical 5-category logic; if it changes, it changes there).

Watch acts autonomously on only the two categories it can safely turn into work: bug → a bug story (Step 4), refinement → a refinement story in the next layer (Step 4). Discovery, opportunity shift, calibration, and process findings are never auto-created regardless of full_auto — they change product intent or workflow, so they always surface to a human (via /aep-reflect; opportunity shifts escalate because they change the bet).

Step 3: Dedupe Against Existing Stories

Before creating anything, check the finding against the current stories section of product-context.yaml (and existing watch_proposals). Skip a finding when:

  • a story already records this source + external_id (watch stamps

watch_origin: { source, external_id } on every story it creates), or

  • an open story's title/description clearly covers the same issue

(same error signature, same endpoint, same metric).

If a matching story is completed/closed and the issue has recurred (new occurrences after completed_at), add a note and surface it as a regression for human attention — a recurrence is new information about old work, not new work.

Postcondition: every surviving finding has no matching open story and no prior watch_origin.{source, external_id}.

Step 4: Write Stories (or Surface Proposals)

For each surviving bug / refinement finding, build a story:

yaml
- id: "watch-<source>-<external_id>"
  title: "<finding title>"
  description: "<finding detail> (auto-discovered by /aep-watch from <source>)"
  type: bug # or refinement
  status: pending
  priority: high # bugs: high; tune by count/severity (see below)
  layer: <active_layer> # bug → current layer; refinement → next layer
  module: <best-effort or unset> # leave unset if the source doesn't localize it
  watch_origin:
    source: "<source>"
    external_id: "<external_id>"
    discovered_at: "<ISO8601>"

Priority / layer rules (mirror `/aep-reflect`):

  • Bugpriority: high, status: pending, in the current/active layer

(escalate to critical when count or severity is high, e.g. crash affecting many users / error_rate over threshold).

  • Refinementstatus: pending in the next layer.
  • Leave module / files_affected unset when the source can't localize them;

dispatch's readiness score routes these through /aep-design first.

Then, per the confirmation policy (Config):

  • Auto-create (full_auto: true OR watch.auto_create: true): append the

story to the stories section — a normal pending story /aep-dispatch scores and /aep-autopilot picks up on the next tick.

  • Surface (default): append the story object to

topology.routing.watch.watch_proposals and print it; a human runs /aep-reflect (or confirms inline) to promote proposals into stories.

Validate + commit (same guardrails as reflect/dispatch — run the validation command in references/yaml-guardrails.md):

bash
npx js-yaml product-context.yaml > /dev/null && echo "YAML OK"
# Resolve $BASE (integration branch) per /aep-git-ref "Integration Branch".
git pull --ff-only origin "$BASE"
git add product-context.yaml
git commit -m "chore: watch — auto-discovered N stories from <sources>"
git push origin "$BASE"

Append a changelog entry (type: watch) summarizing findings ingested, classified, deduped, and created vs. proposed. Postcondition: npx js-yaml product-context.yaml exits 0 and the commit is pushed to $BASE.


Driver

/aep-watch is a continuous/scheduled monitor on the same driver matrix as /aep-autopilot: resolve the host's driver with executor detect() and the driver × backend matrix in /aep-executor references/backends.md, using watch.interval as the interval.

  • Claude Code — `/loop <interval>` (long-lived, in-session): /loop 30m /aep-watch tick.

The session stays alive, so any spawned CHECK delegate is a session-bound native-bg-subagent.

  • Codex — `codex exec` cron/launchd (ephemeral, OS-scheduled): schedule

/aep-watch tick externally (launchd StartInterval, cron, or a while … sleep loop), one cheap one-shot per tick, with OS-bound workers (codex-exec). AEP prints the snippet; it does not install the scheduler.

/aep-watch tick runs one pass of the four-step loop and exits. /aep-watch stop cancels the driver (/loop cancel, or remove the cron/launchd job).


Cross-References

  • /aep-reflectStep 2 classifier (bug / refinement / discovery / …),

reused here; the human-in-the-loop counterpart to watch.

  • references/telemetry-ingestion.md — the finding record + per-source adapters

used by Step 1 (shared with /aep-reflect Step 1).

  • /aep-dispatch — consumes the stories watch creates (scoring, readiness, WIP).
  • /aep-autopilot — the orchestrator pattern, driver matrix, liveness probe, and

main-workspace boundary watch mirrors; picks up watch-created stories next tick.

del mismo repositorio

Más Skills

Todos los Skills