Skip to content

Sandman active detector: stale assignments + recent open PRs #10219

@neo-opus-4-7

Description

@neo-opus-4-7

Context

Sub of #10214. See parent epic for architectural rationale. This ticket focuses on the implementation delta for Sub 5 - extending the DreamService / sandman substrate with an active stale-assignment detector + recent-open-PRs listing in the sandman_handoff.md output.

Historical blocker note: the original ticket treated the entire #10030 concept-ontology epic as the prerequisite. Current source reality narrows that prerequisite to reliable Sandman handoff generation through GoldenPathSynthesizer.synthesizeGoldenPath(), which exists today and must be re-verified during intake.

The Problem

Sub 1 of #10214 (ticket-intake 7-day rule) is the passive reassignment substrate - it fires when an agent attempts pickup. It doesn't surface stale assignments proactively. Agents on boot have no visibility into reassignment opportunities OR into recent PRs needing review.

Sandman is the existing architectural pattern for boot-time "here's what changed while you were asleep" signal. Adding two detector passes to the DreamService output extends this capability without new substrate.

The Architectural Reality

DreamService writes sandman_handoff.md (per AGENTS_STARTUP.md §6 Memory Core check). Currently contains REM-derived strategic priorities + topological alerts. Adding two sections:

## Stale Assignment Candidates

Tickets with an assignee and no qualifying activity in >=7 days:

- #N - <title> - assignee @X - last activity <timestamp> (N days ago)
- ...

## Recent Open PRs

5 most recent open PRs:

- #N - <title> - author @X - opened <timestamp> - cross-family reviewed: yes/no
- ...

Detector logic:

  • Stale assignments: iterate resources/content/issues/*.md where frontmatter has non-empty assignees and state === 'OPEN'. For each, compute lastQualifyingActivity (per Sub 1's definition - assignee comment OR maintainer ack). If now - lastQualifyingActivity >= 7 days, flag.
  • Recent PRs: list top 5 PRs by createdAt desc where state === 'OPEN'. Include cross-family review status (per #10208 mandate) as signal.

Current source-anchor update (2026-05-28): the handoff is currently generated by GoldenPathSynthesizer.synthesizeGoldenPath(), with DreamService.synthesizeGoldenPath() delegating to it. aiConfig.handoffFilePath defaults to resources/content/sandman_handoff.md. Existing repo-specific enrichment already includes ## Active PR Cycle State via fetchOpenPRs() and ## Latest Priority Backlog via local issue/graph state; #10219 should extend or reuse those enrichment paths rather than introduce a second handoff writer.

The Fix

  1. Extend the GoldenPathSynthesizer handoff render path with two detector passes:
    • Stale-assignment pass iterates local synced issue state, applies the 7-day qualifying-activity rule from Sub 1
    • Recent-PRs pass either reuses/extends Active PR Cycle State or emits a deterministic capped recent-open-PR section with cross-family review status
  2. Output both signals into sandman_handoff.md alongside existing repo-enrichment content.
  3. Optional bonus: auto-apply status: needs-reassignment label on detected stale assignments only if a later opt-in config/ticket proves that mutation path is safe. First-pass Sandman active detector: stale assignments + recent open PRs #10219 remains visibility-only.

Contract Ledger

Surface-Anchor V-B-A used for this ledger (2026-05-28):

  • ai/services/graph/GoldenPathSynthesizer.mjs owns fetchOpenPRs(), synthesizeGoldenPath(), ## Active PR Cycle State, ## Latest Priority Backlog, and the final write to aiConfig.handoffFilePath.
  • ai/daemons/orchestrator/services/DreamService.mjs delegates its Golden Path phase to GoldenPathSynthesizer.synthesizeGoldenPath().
  • ai/mcp/server/memory-core/config.template.mjs sets handoffFilePath to resources/content/sandman_handoff.md.
  • learn/agentos/decisions/0014-cloud-deployment-topology-and-scheduler-task-taxonomy.md classifies the Active PR Cycle State and Latest Priority Backlog enrichments as Neo-maintainer-repo-specific and graceful-degrade, so this ticket must preserve that deployment boundary.
Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
resources/content/sandman_handoff.md stale-assignment signal #10219 + #10214 Sub 1 7-day reassignment rule Add a deterministic ## Stale Assignment Candidates section when repo enrichment is enabled; list assigned open issues whose last qualifying activity is >=7 days old, sorted by oldest qualifying activity first, with an explicit empty-state when no candidates exist. If issue-state input cannot be read, log a warning and continue handoff generation without failing the Golden Path render; malformed issue records are skipped with defensive logging. Update learn/agentos/DreamPipeline.md or the handoff consumer doc that owns the Sandman output schema. Focused GoldenPathSynthesizer unit coverage with injected issue fixtures for stale, fresh, unassigned, closed, malformed, and empty-state cases.
Stale-assignment detector input ticket-intake 7-day rule + local synced issue substrate Use local synced issue/graph state, not live GitHub network calls, for open issue state, assignees, labels, and qualifying activity evidence. The qualifying-activity definition must match the ticket-intake rule: assignee comment OR maintainer in-progress acknowledgement. Missing assignee, missing open state, missing qualifying-activity evidence, or needs-re-triage should not produce a candidate. Do not mutate assignments. JSDoc on the detector helper plus the same operator doc as the output section. Unit test proving the 7-day threshold and maintainer/assignee activity semantics, including boundary timestamps.
Recent open PR signal / Active PR Cycle State Existing GoldenPathSynthesizer.fetchOpenPRs() + #10208 cross-family review mandate Prefer extending/reusing the existing ## Active PR Cycle State section rather than adding duplicate PR-cycle output. If a distinct recent-open-PR list is still chosen, fetchOpenPRs() must include createdAt and the render must cap to the top 5 open PRs by createdAt desc with cross-family-review status. fetchOpenPRs() failure remains graceful-degrade: log warning and omit repo enrichment, with no hard dependency on GitHub during unit tests. Non-agent PRs may be included only if the implementation explicitly documents that broader consumer choice. Update handoff schema docs if a new heading is introduced; otherwise document the extension in GoldenPathSynthesizer JSDoc/test names. Unit test with mocked fetchOpenPRs() proving ordering, cap, cross-family review flag, and graceful failure.
Repo-enrichment gate ADR 0014 cloud scheduler taxonomy + existing synthesizeGoldenPath({ repoEnrichmentEnabled = true }) parameter Keep stale-assignment and recent-PR sections behind the existing repo-enrichment boundary or an equivalent explicit config gate; tenant/cloud deployments should not emit Neo-maintainer-repo-specific noise when repo enrichment is disabled. repoEnrichmentEnabled: false omits both new signals and preserves the core Golden Path output. ADR 0014 already owns the deployment taxonomy; add only a small doc note if a new config is introduced. Unit test for repoEnrichmentEnabled: false proving no new repo-specific sections are emitted.
Optional reassignment label mutation #10219 out-of-scope boundary + ticket-intake 7-day rule No label mutation in the first implementation. If pursued later, require an explicit opt-in config and label existence verification before mutation. Default behavior is visibility-only. If the label is missing or config disabled, no mutation occurs. Future ticket only; no first-pass docs requirement beyond out-of-scope note. Not part of #10219 first-pass evidence; future mutation ticket must provide integration-level proof.

Acceptance Criteria

  • DreamService/GoldenPathSynthesizer iterates open issues and flags stale-assignment candidates in sandman_handoff.md
  • Stale-assignment definition matches Sub 1's qualifying-activity rule (reuse the definition; do not diverge)
  • Top 5 open PRs are listed or the existing Active PR Cycle State is extended with equivalent recent-open-PR/cross-family-review visibility
  • Repo-specific sections respect the existing repo-enrichment/deployment boundary and degrade gracefully when local issue or GitHub PR input is unavailable
  • Unit tests cover stale/fresh assignment detection, recent-PR ordering/capping or Active PR Cycle State extension, and enrichment-disabled behavior
  • (Optional/future) auto-label status: needs-reassignment remains out of scope unless a separate opt-in mutation ticket is filed

Out of Scope

  • Auto-reassignment (mutating assignments without agent action) - out of scope; too aggressive
  • First-pass auto-label mutation - visibility-only is the safe Sandman active detector: stale assignments + recent open PRs #10219 shape
  • Notification push (email, slack) - the sandman substrate is boot-time polling; push is separate substrate
  • PR age filtering beyond "top 5 most recent" - could extend to "all PRs whose author is an agent identity" but that's feature-creep; keep initial simple
  • Integration with notificationPreview from Sub 4 - different data paths; could consolidate later

Blocked By

  • Narrowed prerequisite: reliable Sandman handoff generation through GoldenPathSynthesizer.synthesizeGoldenPath(). The original broad #10030 dependency should not be read as requiring the entire concept-ontology epic to close before this ticket can proceed; intake should verify current source reality before implementation.

Related

  • Parent epic: #10214
  • Passive counterpart: Sub 1 of #10214 (codified 7-day rule in ticket-intake) - this ticket is the active surfacer
  • Substrate dependency: reliable Dream Pipeline / Sandman handoff generation
  • Adjacent: #10208 (cross-family mandate - Recent-PRs pass can surface unreviewed-by-cross-family as signal)

Handoff Retrieval Hints

  • query_raw_memories(query="sandman active detector stale assignment recent open PRs")
  • query_raw_memories(query="sandman_handoff.md DreamService extension 7-day rule")
  • query_summaries(query="agent operational hygiene active detector sandman")

Known contributing sessions:

  • ae546a40-2133-482f-85a6-779fdf6757b2 (epic authoring session)

Metadata

Metadata

Assignees

Labels

aiarchitectureArchitecture related issuesenhancementNew feature or requestmodel-experienceIssues related to Model Experience (MX), agent workflows, and AI friction

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions