---
id: 10219
title: 'Sandman active detector: stale assignments + recent open PRs'
state: CLOSED
labels:
  - enhancement
  - ai
  - architecture
  - model-experience
assignees:
  - neo-gpt
createdAt: '2026-04-22T21:56:22Z'
updatedAt: '2026-06-07T17:20:28Z'
githubUrl: 'https://github.com/neomjs/neo/issues/10219'
author: neo-opus-ada
commentsCount: 3
parentIssue: 10214
subIssues:
  - '[x] 12126 Derive GoldenPathSynthesizer swarm identities from identityRoots'
subIssuesCompleted: 1
subIssuesTotal: 1
blockedBy:
  - '[x] 10030 [Epic] Concept Ontology & Semantic Gap Inference'
blocking:
  - '[x] 12126 Derive GoldenPathSynthesizer swarm identities from identityRoots'
closedAt: '2026-05-31T11:55:07Z'
---
# Sandman active detector: stale assignments + recent open PRs

# 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:

```markdown
## 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 #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 #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)

## Timeline

- 2026-04-22T21:56:24Z @neo-opus-ada added the `enhancement` label
- 2026-04-22T21:56:24Z @neo-opus-ada added the `ai` label
- 2026-04-22T21:56:50Z @neo-opus-ada added parent issue #10214
- 2026-04-22T21:56:53Z @neo-opus-ada marked this issue as being blocked by #10030
- 2026-04-27T10:59:27Z @neo-opus-ada cross-referenced by #10214
- 2026-04-27T11:04:53Z @neo-opus-ada cross-referenced by PR #10415
- 2026-04-27T11:24:23Z @neo-opus-ada cross-referenced by PR #10417
- 2026-05-28T01:17:35Z @neo-gpt added the `architecture` label
- 2026-05-28T01:17:35Z @neo-gpt added the `model-experience` label
### @neo-gpt - 2026-05-28T01:17:47Z

**Input from GPT-5.5 (Codex Desktop):**

> ● Triaged per `ticket-triage` skill. Applied secondary labels: `architecture`, `model-experience`.
> 
> Retrospective six-stage challenge passed for labeling:
> 
> - Premise: real — agents lack proactive boot-time visibility into stale assignments and recent PR review opportunities; current Sandman handoff is the existing boot-time substrate.
> - Prescription: right layer — passive reassignment remains in `ticket-intake`; active surfacing belongs in DreamService / Sandman handoff output.
> - Substrate: daemon/handoff layer, not a new polling service.
> - Consumer: next agent boot, Sandman readers, and reviewers looking for active PR-cycle state.
> - Service boundary: keep first pass to surfacing output; avoid auto-reassignment and avoid auto-label unless opt-in.
> - Decision-record impact: no ADR change found; this inherits #10214 and the current DreamService/Sandman substrate.
> 
> Assignment disposition: proceeding to `ticket-intake` now; no branch or file edits yet.

### @neo-gpt - 2026-05-28T01:18:31Z

**Input from GPT-5.5 (Codex Desktop):**

> ● ## Ticket Intake Halt — Contract Ledger required before implementation
> 
> Intake verdict: `needs-contract-alignment`.
> 
> I am not self-assigning or starting a branch yet. This ticket changes a consumed handoff surface (`sandman_handoff.md`) that agents parse at boot, and it adds new sections with operational semantics. Per `ticket-intake` Contract Completeness Sweep, that needs a Contract Ledger before code work.
> 
> Validated context:
> 
> - Parent #10214 now has GPT epic-review greenlight: https://github.com/neomjs/neo/issues/10214#issuecomment-4560023338
> - The broad `blocked_by #10030` premise is stale/narrowable: current source has `DreamService.executeRemCycle()` and `GoldenPathSynthesizer.synthesizeGoldenPath()`, so the relevant prerequisite is reliable Sandman handoff generation, not closure of the entire concept-ontology epic.
> - The work remains architecturally valid, but the child body only gives prose and sample Markdown, not a matrix of consumed surfaces.
> 
> Required before pickup:
> 
> | Surface | Before | After | Consumer |
> |---|---|---|---|
> | `resources/content/sandman_handoff.md` stale-assignment section | no proactive stale-assignment section | deterministic `## Stale Assignment Candidates` section or empty-state behavior | booting agents / Sandman readers |
> | `resources/content/sandman_handoff.md` recent-PR section | active PR cycle state exists, but no simple recent-open-PR list with cross-family-review flag | deterministic recent PR listing or explicit decision to reuse/extend existing `Active PR Cycle State` | booting agents / reviewers |
> | stale-assignment detector input | unspecified issue-state source | exact source of open issues + assignees + last qualifying activity, preferably local synced issue markdown / graph state with test injection | DreamService / GoldenPathSynthesizer tests |
> | cross-family-review status input | unspecified PR source | exact source and fallback behavior for review status, with no hard network dependency in unit tests | handoff writer + agents |
> 
> Once this ledger (or equivalent body matrix) is added, #10219 can proceed as the remaining implementation lane under #10214. No branch, no file edits, no assignment from me in this pass.

### @neo-gpt - 2026-05-28T04:45:50Z

**Input from GPT-5 (Codex Desktop):**

> ● ## Contract Ledger Backfilled
> 
> Backfilled the #10219 issue body with a Contract Ledger after live Surface-Anchor V-B-A.
> 
> Validated anchors:
> 
> - `ai/services/graph/GoldenPathSynthesizer.mjs` owns `fetchOpenPRs()`, `synthesizeGoldenPath()`, `## Active PR Cycle State`, `## Latest Priority Backlog`, and the final `aiConfig.handoffFilePath` write.
> - `ai/daemons/orchestrator/services/DreamService.mjs` delegates the Golden Path phase to `GoldenPathSynthesizer.synthesizeGoldenPath()`.
> - `ai/mcp/server/memory-core/config.template.mjs` defaults `handoffFilePath` to `resources/content/sandman_handoff.md`.
> - ADR 0014 classifies Active PR Cycle State / Latest Priority Backlog as Neo-maintainer-repo-specific enrichments that must gracefully degrade for cloud/tenant deployments.
> 
> No assignment or branch was taken in this pass. This addresses the prior `needs-contract-alignment` intake halt and leaves #10219 ready for a fresh ticket-intake / self-selection pass.

- 2026-05-28T04:50:17Z @neo-gpt assigned to @neo-gpt
- 2026-05-28T05:00:23Z @neo-gpt cross-referenced by PR #12125
- 2026-05-28T05:18:26Z @neo-gpt cross-referenced by #12126
- 2026-05-28T05:18:36Z @neo-gpt added sub-issue #12126
- 2026-05-28T05:20:38Z @neo-gpt marked this issue as blocking #12126
- 2026-05-31T11:37:54Z @neo-gpt referenced in commit `7d79155` - "feat(ai): surface Sandman stale assignment candidates (#10219)"
- 2026-05-31T11:55:07Z @tobiu referenced in commit `b87ba6c` - "feat(ai): surface Sandman stale assignment candidates (#10219) (#12125)

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-31T11:55:07Z @tobiu closed this issue
- 2026-05-31T22:18:48Z @neo-opus-ada cross-referenced by PR #12278

