---
id: 11693
title: Golden Path ranks closed Discussions as open
state: CLOSED
labels:
  - bug
  - ai
  - architecture
  - model-experience
assignees:
  - neo-gpt
createdAt: '2026-05-20T20:02:42Z'
updatedAt: '2026-05-20T20:49:01Z'
githubUrl: 'https://github.com/neomjs/neo/issues/11693'
author: neo-gpt
commentsCount: 0
parentIssue: null
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-20T20:49:01Z'
---
# Golden Path ranks closed Discussions as open

## Context

Operator V-B-A surfaced 2026-05-20: `resources/content/discussions/**/discussion-*.md` now carries `closed` / `closedAt` frontmatter after #11554 / #11573, but `resources/content/sandman_handoff.md` still ranked closed Discussions in the Computed Golden Path top 5.

This ticket exists because the producer-side sync fix is present, but the graph consumer still appears to ignore the new lifecycle state.

Duplicate sweep:
- #9809 added Discussion nodes to the Golden Path, but it predates discussion closed-state frontmatter and treated active Discussions as the target problem.
- #11554 synced `closed` / `closedAt` to Discussion markdown.
- #11573 fixed metadata persistence / frontmatter integrity so the fields actually land in synced markdown.
- No existing ticket found for the downstream `IssueIngestor` / Golden Path consumer still forcing Discussion nodes open.

## The Problem

Closed Discussions are still represented in the Native Edge Graph as `state: 'OPEN'`, so the Golden Path ranking can promote already-graduated / closed archaeological discussions as active next work.

Empirical sample from the current checkout:

```yaml
# resources/content/discussions/chunk-1/discussion-11168.md
closed: true
closedAt: '2026-05-11T00:19:55Z'
```

But Memory Core graph lookup currently reports:

```json
{"id":"discussion-11168","type":"DISCUSSION","state":"OPEN"}
```

The same mismatch appears for `discussion-10520` (`closed: true` on disk; graph node state `OPEN`). Both appeared in the Golden Path top 5 in the current `sandman_handoff.md` output.

## The Architectural Reality

`DiscussionSyncer` now emits the lifecycle fields correctly:

- `ai/services/github-workflow/sync/DiscussionSyncer.mjs:242-250` writes `closed` and `closedAt` into frontmatter.

`IssueIngestor` is the graph projection surface for Discussions:

- `ai/daemons/services/IssueIngestor.mjs:265-349` reads `resources/content/discussions` and upserts `DISCUSSION` nodes.
- `IssueIngestor.mjs:308-315` currently hard-codes every Discussion node to `state: 'OPEN'` with the comment: `Discussions are treated as perpetually open for semantic traversal`.

`GoldenPathSynthesizer` then filters candidate nodes by open state:

- `ai/daemons/services/GoldenPathSynthesizer.mjs:190-202` selects only nodes whose `$.properties.state` or `$.state` is `OPEN`.

Therefore the sync layer carries the truth, but graph projection overwrites the lifecycle state before the ranking query sees it.

## The Fix

Teach `IssueIngestor.ingestDiscussionStates()` to derive Discussion graph state from frontmatter:

- `meta.closed === true` -> graph `state: 'CLOSED'` and `properties.closed = true`, `properties.closedAt = meta.closedAt || null`.
- otherwise -> graph `state: 'OPEN'` and `properties.closed = false`, `properties.closedAt = null`.
- Preserve `category`, `updatedAt`, and title behavior.
- Ensure the vector metadata for Discussions carries lifecycle metadata where useful (`closed`, `closedAt`, `type: 'DISCUSSION'`) so future graph/vector consumers do not re-derive from raw markdown.
- Add regression coverage that a closed Discussion markdown fixture does not become an OPEN graph candidate.

## Contract Ledger Matrix

| Target Surface | Source of Authority | Proposed Behavior | Fallback / Edge Case | Docs | Evidence |
|---|---|---|---|---|---|
| `IssueIngestor.ingestDiscussionStates()` graph node state | Discussion frontmatter emitted by #11554 / #11573 | Use `closed` to set `DISCUSSION` node state to `OPEN` or `CLOSED` | Missing `closed` preserves legacy `OPEN` behavior but records `closed: false` | JSDoc / inline comment replacing perpetual-open rationale | Unit test with open + closed Discussion fixtures |
| Golden Path candidate set | `GoldenPathSynthesizer` open-state filter | Closed Discussion nodes are excluded from actionable top 5 | Open Discussions still surface normally | Test description / ticket refs | Unit test or integration-style focused spec proves closed node is filtered |
| `sandman_handoff.md` operator output | Native Edge Graph + Golden Path ranking | Closed archaeological Discussions no longer appear as current strategic recommendations | Historical closed discussions remain searchable in KB / resources content | Ticket body and PR body | Manual post-fix `runSandman` or targeted synthesizer evidence |

## Acceptance Criteria

- [ ] `IssueIngestor.ingestDiscussionStates()` maps `meta.closed === true` to `state: 'CLOSED'` instead of hard-coded `OPEN`.
- [ ] Discussion node properties include `closed` and `closedAt` so graph inspection exposes the lifecycle state.
- [ ] Regression test covers at least one open Discussion and one closed Discussion fixture.
- [ ] Golden Path synthesis excludes closed Discussion nodes from the actionable top-5 candidate set while preserving open Discussions.
- [ ] Existing closed nodes such as `discussion-11168` and `discussion-10520` are corrected after the next graph ingestion / Sandman run.

## Out of Scope

- Changing DiscussionSyncer. The producer-side frontmatter fields already exist.
- Removing closed Discussions from the Knowledge Base. Closed discussions remain valuable archaeological context.
- Closing currently open Discussions. This ticket only fixes graph projection and ranking.
- Broad IssueIngestor freshness/cadence changes. The two-stage sync-then-ingest cadence remains separate.

## Avoided Traps

- **Dropping closed Discussions entirely.** Rejected: closed discussions still carry design archaeology and should remain searchable. They just should not rank as active Golden Path work.
- **Changing GoldenPathSynthesizer to special-case IDs.** Rejected: the ranking layer already has a state filter; the bug is that the graph projection lies about Discussion state.
- **Reintroducing a `state` enum into DiscussionSyncer.** Rejected: GitHub Discussions expose `closed` / `closedAt`; the graph projection can translate that into the graph's `OPEN` / `CLOSED` state convention.

## Related

- #9809 — original Discussion-to-Golden-Path ingestion.
- #11554 — sync `closed` / `closedAt` to Discussion frontmatter.
- #11573 — post-merge verification gap and sync metadata persistence fix.
- `resources/content/sandman_handoff.md` current Golden Path output.

Origin Session ID: d13c94dd-e721-4e28-ac9e-4d0b3c0f66de
Handoff Retrieval Hint: `query_raw_memories("closed Discussions Golden Path IssueIngestor state OPEN sandman_handoff")`

## Timeline

- 2026-05-20T20:02:43Z @neo-gpt added the `bug` label
- 2026-05-20T20:02:43Z @neo-gpt added the `ai` label
- 2026-05-20T20:02:44Z @neo-gpt added the `architecture` label
- 2026-05-20T20:02:44Z @neo-gpt added the `model-experience` label
- 2026-05-20T20:09:48Z @neo-gpt cross-referenced by PR #11694
- 2026-05-20T20:26:44Z @neo-gpt assigned to @neo-gpt
- 2026-05-20T20:35:00Z @neo-gpt cross-referenced by PR #11695
- 2026-05-20T20:49:01Z @tobiu referenced in commit `50c40e5` - "fix(ai): preserve discussion lifecycle state (#11693) (#11695)

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-20T20:49:01Z @tobiu closed this issue

