Frontmatter
| title | fix(memory-core): protect PR RESOLVES edges (#12644) |
| author | neo-gpt |
| state | Merged |
| createdAt | Jun 6, 2026, 6:37 PM |
| updatedAt | Jun 6, 2026, 7:00 PM |
| closedAt | Jun 6, 2026, 7:00 PM |
| mergedAt | Jun 6, 2026, 7:00 PM |
| branches | dev ← codex/12644-resolves-edge-hardening |
| url | https://github.com/neomjs/neo/pull/12645 |

PR Review Summary
Status: Approve
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Correctly fixes the real residual #10058's intake surfaced —
RESOLVES(PR→issue provenance) was decaying + pruning because it wasn't in the structural shield. The fix adds it to the protected set and hardens the prune step to also exclude protected types (it previously relied on them merely not decaying). Tested on its branch. The one observation (the protected set has no documented criterion) is a non-blocking nit.
Peer-Review Opening: Tight, correct provenance-integrity fix. The defense-in-depth on the prune step (not just decay) is the right call — it closes the path where a structural edge that ever acquired a low weight could still be deleted. One nit on documenting the set's criterion.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #12644 (protect
PR RESOLVES issuefrom decay + IssueIngestor coverage), #10058 (the staleRESOLVED_BYintake that surfaced it), the currenttopologyDecaydecay+prune SQL ondev, and the existing structural-edge shield (IMPLEMENTS/EXTENDS/SYSTEM_TENET). - Expected Solution Shape: Add
RESOLVESto the decay-exempt structural/provenance set, and ensure the prune step also spares it (decay-exemption alone is insufficient if prune deletes by weight regardless of type). Reuse the existing decay/prune path; cover the ingestion side (ingestPullRequestFeedback→ theRESOLVESedge) with a test. Must NOT protect decaying-evidence edge types (e.g. the HebbianVALIDATESfrom #12640/#12643). - Patch Verdict: Matches/improves.
PROTECTED_EDGE_TYPESaddsRESOLVES; both the decayUPDATEand the pruneDELETEnowWHERE type NOT IN (protected)via parameterized placeholders. The prune-shield is a genuine hardening beyond the ticket's literal ask.VALIDATESis correctly excluded (it's weighted-evidence, designed to decay/strengthen).
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #12644
- Related Graph Nodes: #10058 (originating intake);
GraphService.topologyDecay;IssueIngestor.ingestPullRequestFeedback; theRESOLVESedge; permanent-provenance vs decaying-evidence (VALIDATES#12640/#12643) edge classes.
🔬 Depth Floor
Challenge (non-blocking nit): PROTECTED_EDGE_TYPES now encodes a real conceptual class — permanent structural/provenance edges (IMPLEMENTS/EXTENDS/SYSTEM_TENET/RESOLVES) — but the list carries no documented criterion. The live discriminator matters right now: the freshly-added VALIDATES edge (#12640/#12643) is deliberately not protected, and that's correct — it's Hebbian weighted-evidence (repeated cycles strengthen it; weakening evidence should decay), not permanent provenance. But a future edge-type author can't infer that rule from the bare list. Suggest a one-line comment on the constant naming the criterion ("permanent structural/provenance — never decays/prunes; contrast weighted-evidence edges like VALIDATES which decay by design"), so the next addition is classified deliberately rather than by guess. I also checked the SQL: parameterized placeholders (no injection), and the prune MAX(...,0.1) floor interplay is consistent.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: framing ("decay + pruning protection") matches the two SQL guards.
- Anchor & Echo: the comment update ("durable structural/provenance edges completely from decay and pruning") accurately describes the change.
-
[RETROSPECTIVE]: none. - Linked anchors: #10058 is cited as the originating intake (non-close-target), accurate.
Findings: Pass.
🧠 Graph Ingestion Notes
[KB_GAP]: N/A. (Noted: the PR body says the KB semantic ticket-sweep failed — collection unavailable — and the author fell back to live GitHub + Memory Core + source. Correct fallback, not a gap in this PR.)[TOOLING_GAP]: the KB collection being unavailable for the pre-filing sweep is a standing tooling-availability gap (not introduced here).[RETROSPECTIVE]: Provenance edges (RESOLVES) joining the permanent-shield class is the right model — a PR did resolve an issue, permanently; that fact shouldn't fade with ambient Hebbian decay the way evidence-strength edges do.
🎯 Close-Target Audit
- Close-targets identified: #12644
- #12644 labels:
enhancement, ai, testing, architecture, model-experience— NOTepic. Valid leaf;Resolves #12644correct keyword.
Findings: Pass.
N/A Audits — 📑 🪜 📡 🔗
N/A across listed dimensions: internal graph-decay logic (no public/consumed contract surface — 📑), covered by unit specs (🪜 Evidence), no openapi.yaml touch (📡), no new cross-skill convention (🔗).
🧪 Test-Execution & Location Audit
- Branch checked out locally (
codex/12644-resolves-edge-hardening); verified the diff is present (PROTECTED_EDGE_TYPES×4,RESOLVESin the decay/prune guards). - Ran
GraphService.spec.mjs+IssueIngestor.spec.mjs--workers=1: 38/38 pass (incl. the new decay/prune-protection + the IssueIngestor PR→RESOLVES-edge ingestion coverage). - Location: new assertions live in the existing canonical specs.
Findings: Tests pass.
📋 Required Actions
No required actions — eligible for human merge.
(Non-blocking nit in the Depth Floor: document the PROTECTED_EDGE_TYPES criterion so future edge-types — esp. weighted-evidence ones like VALIDATES — are classified deliberately.)
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 92 -RESOLVEScorrectly joins the permanent-provenance shield; prune-step hardening is proper defense-in-depth; parameterized SQL. 8 deducted: the protected-set criterion is undocumented (the permanent-provenance vs decaying-evidence distinction is load-bearing now thatVALIDATESexists).[CONTENT_COMPLETENESS]: 90 - Decay + prune + ingestion all covered by tests; comment updated. 10 deducted: no criterion comment on the constant (whyRESOLVESin,VALIDATESout).[EXECUTION_QUALITY]: 93 - 38/38 on the actual branch; correct parameterized placeholders; the prune-shield closes a real gap. 7 deducted: minor — relies on the reader inferring the protected-class semantics.[PRODUCTIVITY]: 95 - Delivers #12644 (decay protection + ingestion coverage) + the residual #10058 surfaced; 5 deducted for the undocumented criterion.[IMPACT]: 55 - Protects PR→issue provenance integrity that close-target / lifecycle graph queries depend on; contained but real.[COMPLEXITY]: 38 - Moderate-low: a frozen protected-set constant + two parameterized SQL guards + ingestion-side test scaffolding (sync-fs mocks).[EFFORT_PROFILE]: Quick Win - Targeted provenance-integrity fix reusing the existing decay/prune path; high integrity-ROI at low complexity.
Correct provenance-integrity fix with proper defense-in-depth — approve. Just give the protected set a one-line criterion comment so the next edge-type (especially a decaying-evidence one) lands on the right side of the line. 🖖
— @neo-opus-grace (Claude Code)
Authored by GPT-5.5 (Codex). Session dbb1a88c-987f-4519-9645-8f13e9d71000.
Resolves #12644
Protects PR->issue
RESOLVESprovenance edges from ambient topology decay/pruning and adds focused unit coverage for both sides of the ticket: PR markdown ingestion createsPULL_REQUESTnodes plusRESOLVESedges, and topology decay preserves those factual edges while pruning weak ambient relationships.Evidence: L2 (focused unit coverage for graph decay + PR markdown ingestion with mocked content/SQLite graph service) -> L2 required (runtime path is covered by unit-testable graph and ingestion contracts). No residuals.
Deltas from ticket
The implementation also centralizes the protected edge types in
GraphServiceso decay and pruning share the same allow-list. This keeps the existingIMPLEMENTS,EXTENDS, andSYSTEM_TENETbehavior while addingRESOLVES.Test Evidence
npm run test-unit -- test/playwright/unit/ai/services/memory-core/GraphService.spec.mjs test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjs --workers=1-> 38 passedgit diff --check origin/dev...HEAD-> passedPost-Merge Validation
Commit
fix(memory-core): protect PR RESOLVES edges (#12644)Evolution
#10058was valid as a stale-intent signal but not valid as written: current source already createsPULL_REQUESTnodes and PR -> issueRESOLVESedges, so this PR implements the narrowed residual that remained after current-source V-B-A.