Context
Intake of #10058 on 2026-06-06 found that the ticket's literal RESOLVED_BY prescription is stale against current dev, but it exposed a real residual in the current PR-resolution graph path.
Live latest-open sweep: checked the latest 20 open issues on 2026-06-06 before filing. The nearest hit is #10058, now labeled needs-re-triage; no equivalent fresh ticket for current RESOLVES decay protection plus IssueIngestor.ingestPullRequestFeedback() coverage was found. Exact live searches for RESOLVES decay and ingestPullRequestFeedback found no equivalent open implementation ticket. KB semantic ticket sweep was attempted twice and failed because the Knowledge Base collection is unavailable, so this filing relies on live GitHub, Memory Core, and current-source evidence.
The Problem
The modern graph contract is no longer issue -> PR RESOLVED_BY. Current code creates PULL_REQUEST nodes and RESOLVES edges from pr-N -> issue-N, and graph topology traverses inbound and outbound edges. That contract is documented in learn/agentos/DreamPipeline.md.
However, two gaps remain:
GraphService.decayGlobalTopology() protects only IMPLEMENTS, EXTENDS, and SYSTEM_TENET. Factual RESOLVES edges can decay below the pruning threshold, even though a PR resolving an issue is durable provenance.
- The PR ingestion path lacks direct unit coverage proving synced PR markdown creates the
PULL_REQUEST node and RESOLVES edge.
The Architectural Reality
ai/services/ingestion/IssueIngestor.mjs currently reads resources/content/pulls and resources/content/archive/pulls, upserts PULL_REQUEST nodes, and links Resolves|Closes|Fixes #N matches as RESOLVES edges from PR node to issue node.
ai/services/memory-core/GraphService.mjs currently applies global decay with WHERE type NOT IN ('IMPLEMENTS', 'EXTENDS', 'SYSTEM_TENET').
GraphService.getNeighbors() and GraphService.queryNodeTopology() already traverse both inbound and outbound edges, so no parallel RESOLVED_BY relation is needed.
learn/agentos/DreamPipeline.md documents Resolves #N as the canonical shipped-work form while keeping the ingestor liberal for Fixes and Closes.
test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjs does not currently exercise ingestPullRequestFeedback() with PR markdown and close-keyword extraction.
The Fix
- Add
RESOLVES to the protected relationship set in GraphService.decayGlobalTopology() so factual PR-to-issue edges do not decay or prune.
- Add focused unit coverage for
IssueIngestor.ingestPullRequestFeedback() that proves PR markdown creates a PULL_REQUEST node and a RESOLVES edge to the referenced issue.
- Add or update focused unit coverage for decay protection so the protected-edge behavior is executable, not only prose.
- Keep the existing
RESOLVES direction and vocabulary. Do not introduce RESOLVED_BY as a parallel synonym.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
IssueIngestor.ingestPullRequestFeedback() PR markdown ingestion |
Current IssueIngestor.mjs and DreamPipeline.md PR Feedback section |
Upserts a PULL_REQUEST node and links referenced issues with RESOLVES from pr-N to issue-N |
PR files without close keywords still only create the PR node and tag-derived gap nodes |
Existing JSDoc/docs remain valid unless test exposes drift |
Focused unit test with mocked PR markdown |
GraphService.decayGlobalTopology() relationship protection |
Current factual graph relation contract documented in DreamPipeline.md |
RESOLVES edges are excluded from global decay/pruning like other durable structural edges |
Non-durable relationship types continue to decay normally |
Inline comment may mention factual edge class if needed |
Focused GraphService unit assertion |
Graph topology consumers (getNeighbors, queryNodeTopology, query_hybrid_graph) |
Current bidirectional traversal implementation |
Consumers keep seeing PR resolution neighbors via existing RESOLVES edge direction |
No RESOLVED_BY alias is introduced; stale #10058 wording remains re-triage-only |
No docs change expected unless implementation discovers mismatch |
Existing traversal tests plus new edge-ingestion/decay tests |
Decision Record impact
Aligned with the existing Native Edge Graph / Dream Pipeline contract. No ADR amendment required.
Acceptance Criteria
Out of Scope
- Rewriting #10058 literally.
- Introducing a
RESOLVED_BY edge synonym.
- Tightening
Closes parsing semantics; that remains the separate logic question documented by #10755 / PR #11621.
- Backfilling historical graph rows outside the normal ingestion path.
- Changing
get_context_frontier ranking behavior.
Avoided Traps
- Parallel vocabulary drift. Adding
RESOLVED_BY would split a contract that already exists as RESOLVES.
- Docs-only closure. The residual is executable graph behavior; it needs tests.
- A one-line-only cleanup. The decay fix must land with ingestion and decay coverage so the PR proves the factual edge path end to end.
- Stale path inheritance. The current service path is
ai/services/..., not the old pre-M6 MCP service path named in #10058.
Related
- #10058 — source stale ticket; now marked
needs-re-triage with intake comment.
- #9851 / PR #9861 — original PR feedback integration lineage.
- #10755 / PR #11621 — current
RESOLVES convention and liberal ingestor documentation.
Handoff Retrieval Hints
query_raw_memories(query="RESOLVES edges decay protection pull request graph ingestion tests")
query_summaries(query="IssueIngestor PR feedback RESOLVES edge tests")
- Source anchors:
IssueIngestor.ingestPullRequestFeedback, GraphService.decayGlobalTopology, DreamPipeline.md PR Feedback section.
Context
Intake of #10058 on 2026-06-06 found that the ticket's literal
RESOLVED_BYprescription is stale against currentdev, but it exposed a real residual in the current PR-resolution graph path.Live latest-open sweep: checked the latest 20 open issues on 2026-06-06 before filing. The nearest hit is #10058, now labeled
needs-re-triage; no equivalent fresh ticket for currentRESOLVESdecay protection plusIssueIngestor.ingestPullRequestFeedback()coverage was found. Exact live searches forRESOLVES decayandingestPullRequestFeedbackfound no equivalent open implementation ticket. KB semantic ticket sweep was attempted twice and failed because the Knowledge Base collection is unavailable, so this filing relies on live GitHub, Memory Core, and current-source evidence.The Problem
The modern graph contract is no longer
issue -> PR RESOLVED_BY. Current code createsPULL_REQUESTnodes andRESOLVESedges frompr-N -> issue-N, and graph topology traverses inbound and outbound edges. That contract is documented inlearn/agentos/DreamPipeline.md.However, two gaps remain:
GraphService.decayGlobalTopology()protects onlyIMPLEMENTS,EXTENDS, andSYSTEM_TENET. FactualRESOLVESedges can decay below the pruning threshold, even though a PR resolving an issue is durable provenance.PULL_REQUESTnode andRESOLVESedge.The Architectural Reality
ai/services/ingestion/IssueIngestor.mjscurrently readsresources/content/pullsandresources/content/archive/pulls, upsertsPULL_REQUESTnodes, and linksResolves|Closes|Fixes #Nmatches asRESOLVESedges from PR node to issue node.ai/services/memory-core/GraphService.mjscurrently applies global decay withWHERE type NOT IN ('IMPLEMENTS', 'EXTENDS', 'SYSTEM_TENET').GraphService.getNeighbors()andGraphService.queryNodeTopology()already traverse both inbound and outbound edges, so no parallelRESOLVED_BYrelation is needed.learn/agentos/DreamPipeline.mddocumentsResolves #Nas the canonical shipped-work form while keeping the ingestor liberal forFixesandCloses.test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjsdoes not currently exerciseingestPullRequestFeedback()with PR markdown and close-keyword extraction.The Fix
RESOLVESto the protected relationship set inGraphService.decayGlobalTopology()so factual PR-to-issue edges do not decay or prune.IssueIngestor.ingestPullRequestFeedback()that proves PR markdown creates aPULL_REQUESTnode and aRESOLVESedge to the referenced issue.RESOLVESdirection and vocabulary. Do not introduceRESOLVED_BYas a parallel synonym.Contract Ledger Matrix
IssueIngestor.ingestPullRequestFeedback()PR markdown ingestionIssueIngestor.mjsandDreamPipeline.mdPR Feedback sectionPULL_REQUESTnode and links referenced issues withRESOLVESfrompr-Ntoissue-NGraphService.decayGlobalTopology()relationship protectionDreamPipeline.mdRESOLVESedges are excluded from global decay/pruning like other durable structural edgesGraphServiceunit assertiongetNeighbors,queryNodeTopology,query_hybrid_graph)RESOLVESedge directionRESOLVED_BYalias is introduced; stale #10058 wording remains re-triage-onlyDecision Record impact
Aligned with the existing Native Edge Graph / Dream Pipeline contract. No ADR amendment required.
Acceptance Criteria
GraphService.decayGlobalTopology()excludesRESOLVESfrom decay/pruning.RESOLVESedges remain protected while ordinary edges still decay or prune.IssueIngestor.ingestPullRequestFeedback()creates aPULL_REQUESTnode from PR markdown.IssueIngestor.ingestPullRequestFeedback()creates aRESOLVESedge frompr-Ntoissue-Nwhen the PR body contains a close keyword.RESOLVED_BYor change graph traversal direction.GraphServiceandIssueIngestortest surfaces.Out of Scope
RESOLVED_BYedge synonym.Closesparsing semantics; that remains the separate logic question documented by #10755 / PR #11621.get_context_frontierranking behavior.Avoided Traps
RESOLVED_BYwould split a contract that already exists asRESOLVES.ai/services/..., not the old pre-M6 MCP service path named in #10058.Related
needs-re-triagewith intake comment.RESOLVESconvention and liberal ingestor documentation.Handoff Retrieval Hints
query_raw_memories(query="RESOLVES edges decay protection pull request graph ingestion tests")query_summaries(query="IssueIngestor PR feedback RESOLVES edge tests")IssueIngestor.ingestPullRequestFeedback,GraphService.decayGlobalTopology,DreamPipeline.mdPR Feedback section.