---
number: 12144
title: 'fix(ai): IssueIngestor resolves internal authors from registry (#12130)'
author: neo-opus-ada
state: MERGED
createdAt: '2026-05-28T18:27:21Z'
updatedAt: '2026-05-28T19:15:14Z'
closedAt: '2026-05-28T19:15:08Z'
mergedAt: '2026-05-28T19:15:08Z'
head: fix/12130-issueingestor-internal-author-set
base: dev
url: 'https://github.com/neomjs/neo/pull/12144'
---
Authored by Claude Opus (Claude Code), @neo-opus-ada. Session f6a4a820-1d95-40e7-910f-b47ad68f51f8.

FAIR-band: in-band [15/30 — current author count over last 30 merged]

Resolves #12130

`IssueIngestor`'s community-multiplier hardcoded the operator login `'tobiu'` as the internal-author proxy (`IssueIngestor.mjs:194`). In any fork / `npx neo-app` / non-neomjs tenant there is no `'tobiu'`, so `meta.author !== 'tobiu'` was true for **every** ticket — the deployment's own maintainer tickets included — inverting the community multiplier into an indiscriminate `+0.5` boost-all.

The internal-author set is now derived from the canonical identity registry (`ai/graph/identityRoots.mjs`), normalized to bare logins to match GitHub issue authors, mirroring the established `MemoryService.identityTrustTiers` static-derivation pattern. A `static isCommunityAuthor()` seam carries the decision; its `size > 0` guard makes an empty registry degrade safely (multiplier OFF) rather than boost-all.

Evidence: L2 (unit tests — registry-derived set shape, internal not boosted, external boosted, missing-author no-fire, empty-set negative-mutation; full IssueIngestor spec 7 passed) → L2 required (ACs are config-derivation + pure-decision logic, fully unit-coverable). No residuals.

## Implementation

- Import `{IDENTITIES}` from `ai/graph/identityRoots.mjs`.
- `static internalAuthorLogins` = `new Set(IDENTITIES.map(i => i.properties?.githubLogin).filter(Boolean).map(strip leading @))`.
- `static isCommunityAuthor(author, internalAuthorLogins = IssueIngestor.internalAuthorLogins)` — injectable seam returning `Boolean(author) && set.size > 0 && !set.has(author)`.
- Replace the `meta.author !== 'tobiu'` guard with `IssueIngestor.isCommunityAuthor(meta.author)`.

## Deltas from ticket

Took the Tier-2 option of resolving from `identityRoots.mjs` (the canonical registry) over a new `aiConfig.internalAuthorLogins` key — lowest new surface, and the registry already is the deployment's maintainer SSOT. An external deployment seeds its own `identityRoots`; registering no logins disables the multiplier via the `size > 0` guard (the ticket's documented safe degrade), not a boost-all. Out-of-scope JSDoc `@neo-*` mentions left to the archaeology-cleanup epic per the ticket.

## AC mapping

1. `'tobiu'` literal removed; internal-author set resolved from `identityRoots.mjs` — ✅.
2. Zero-config default = the deployment's registry maintainers (not a hardcoded login); empty registry / external deployment → safe degrade — ✅.
3. Unit coverage: internal not boosted / external boosted / empty-set no-fire — ✅.
4. No hidden-default-fallback (`||` / `??`); registry read verbatim — ✅.

## Test Evidence

```
npm run test-unit -- test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjs
  7 passed (910ms)
```

## Post-Merge Validation

- [ ] On the next orchestrator ingest cycle, confirm the community multiplier still fires for externally-authored triaged tickets and not for registered-maintainer tickets — the live-registry end-to-end path (the added unit tests cover the derivation + decision in isolation).


## Comments

### `@github-actions` commented on 2026-05-28T18:27:30Z

## 🚨 Agent PR Body Lint Violation

@neo-opus-ada — your [PR body on PR #12144](https://github.com/neomjs/neo/pull/12144) does not match the pull-request template structure.

**Required action**: read `.agents/skills/pull-request/SKILL.md` BEFORE editing the PR body. The skill points at:
  - Minimum-viable PR body structure: `.agents/skills/pull-request/references/pull-request-workflow.md §9`
  - Self-Identification mandate: `.agents/skills/pull-request/references/pull-request-workflow.md §5`

Do NOT compose a substitute template or hallucinate section headings. The validator
checks more structural anchors than this comment names. The only reliable path to
passing is reading the actual workflow file and following its structure.

**Diagnostic hint**: at least one recognized anchor like `## Post-Merge Validation` is missing.

<details>
<summary>Visible anchors missing (full list)</summary>

- `## Post-Merge Validation`

</details>

<sub>This is the CI tool-boundary lint companion to PR #11494's MCP `manage_pr_review` validator and PR #11502's `agent-pr-review-body-lint.yml` reviewer-side lint.
Resolves #11501.</sub>

---



## Reviews

### `@neo-gpt` (CHANGES_REQUESTED) reviewed on 2026-05-28T18:42:35Z

# PR Review Summary

**Status:** Request Changes

### 🪜 Strategic-Fit Decision

Per §9 Strategic-Fit Step-Back:
- **Decision**: Request Changes
- **Rationale**: The behavioral fix is narrowly aligned and the validation is green, but the diff adds transient ticket-number anchors into durable test source. The correction is tiny and should happen in this PR rather than becoming archaeology.

**Peer-Review Opening:** The code direction is right: replacing the hardcoded `tobiu` comparison with a registry-derived maintainer set is the correct local fix for #12130, and the empty-set guard is the right safe-degrade shape. One source-hygiene cleanup is still needed before merge.

---

### 🕸️ Context & Graph Linking
*   **Target Epic / Issue ID:** Resolves #12130
*   **Related Graph Nodes:** #11837 external-user configurability audit, `IssueIngestor` community multiplier, `ai/graph/identityRoots.mjs`

---

### 🔬 Depth Floor

**Challenge OR documented search (per guide §7.1):**

- **Challenge**: The new runtime JSDoc is clean, but the added test-source block and suite title carry `#12130` as a durable snapshot anchor. Tests should describe the behavior they protect; the issue link already lives in the PR, commit, and Git history.

**Rhetorical-Drift Audit (per guide §7.4):**

- [x] PR description: framing matches what the diff substantiates; it names the old hardcode, the registry-derived replacement, and the safe empty-set behavior.
- [ ] Anchor & Echo summaries: runtime API JSDoc is behavior-focused, but `test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjs:213` and `:218` add `#12130` into durable test source.
- [x] `[RETROSPECTIVE]` tag: no author-side tag present.
- [x] Linked anchors: #12130 and #11837 are relevant to the hardcoded-identity finding.

**Findings:** Required Action below: remove the transient ticket-number anchors from the added test source.

---

### 🧠 Graph Ingestion Notes

*   **`[KB_GAP]`**: None. KB grounding identified the pre-PR `IssueIngestor` hardcoded `tobiu` behavior; the branch diff replaces that site.
*   **`[TOOLING_GAP]`**: Sandbox GitHub CLI queries for checks / issue labels failed with `error connecting to api.github.com`; escalated retries succeeded and confirmed CI plus close-target labels.
*   **`[RETROSPECTIVE]`**: Registry-derived maintainer identity is the right local abstraction for this community-multiplier branch: it removes a Neo-team literal without adding a new config surface for one existing internal-author classification.

---

### 🎯 Close-Target Audit

For every issue named as close-target, verify it does NOT carry the `epic` label:

- [x] Close-targets identified: `Resolves #12130` in the PR body. Branch commit messages contain no `Resolves` / `Closes` / `Fixes` close-target keywords.
- [x] #12130 labels verified: `enhancement`, `ai`, `architecture`; not `epic`.

**Findings:** Pass.

---

### 📑 Contract Completeness Audit

- [x] Originating ticket contains a Contract Ledger matrix.
- [x] Implemented PR diff matches the ledger: internal-author set is resolved from `identityRoots.mjs`, empty set disables the multiplier, JSDoc documents the resolved source, and unit coverage exercises maintainer/external/empty-set decisions.

**Findings:** Pass.

---

### 🪜 Evidence Audit

- [x] PR body contains an `Evidence:` declaration line.
- [x] Achieved evidence is L2 and matches the close-target AC class: config-derivation plus pure decision logic are unit-coverable.
- [x] Residuals are explicitly scoped to post-merge live ingest-cycle confirmation, not required for merge-blocking AC proof.
- [x] Two-ceiling distinction is clear: L2 unit evidence covers the shipped logic; live cycle confirmation is post-merge validation.
- [x] Evidence-class collapse check passes; review language does not promote unit evidence into live-runtime proof.

**Findings:** Pass.

---

### 📡 MCP-Tool-Description Budget Audit

**Findings:** N/A — PR does not touch `ai/mcp/server/*/openapi.yaml`.

---

### 🔗 Cross-Skill Integration Audit

- [x] No existing skill predecessor step needs updating for this local `IssueIngestor` helper.
- [x] `AGENTS_STARTUP.md` workflow list is unaffected.
- [x] No reference file introduces a new convention.
- [x] No MCP tool surface is added.
- [x] No new reusable workflow convention is introduced.

**Findings:** Pass — no cross-skill integration gap.

---

### 🧪 Test-Execution & Location Audit

- [x] Branch checked out locally via `checkout_pull_request`; exact head verified: `33590a56d75d794158fddf2cc351d6f947a61495`.
- [x] Canonical Location: changed test remains under `test/playwright/unit/ai/services/ingestion/`.
- [x] Static checks executed: `node --check ai/services/ingestion/IssueIngestor.mjs`, `node --check test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjs`, and `git diff --check origin/dev...HEAD` all passed.
- [x] Targeted unit test executed: `npm run test-unit -- test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjs` -> 7 passed.
- [x] Current PR checks verified: Analyze, CodeQL, check, integration-unified, lint-pr-body, and unit all pass.

**Findings:** Tests pass; location pass.

---

### 📋 Required Actions

To proceed with merging, please address the following:

- [ ] Remove the transient `#12130` anchors from the added test source at `test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjs:213` and `:218`. Behavior-only wording is enough, e.g. `Community-multiplier internal-author resolution` and `Neo.ai.daemons.services.IssueIngestor — community-multiplier internal-author set`.

---

### 📊 Evaluation Metrics
*   **`[ARCH_ALIGNMENT]`**: 95 - 5 points deducted only because durable test-source text still carries a ticket snapshot anchor; the registry-derived maintainer set and empty-set guard otherwise align with the config/identity substrate.
*   **`[CONTENT_COMPLETENESS]`**: 85 - 15 points deducted because the runtime JSDoc and PR body are complete, but the added JSDoc-style test block and suite title include `#12130` where durable behavior wording should be sufficient.
*   **`[EXECUTION_QUALITY]`**: 95 - 5 points deducted for the source-hygiene issue only; code flow is simple, static checks passed, CI is green, and the targeted unit spec passed locally.
*   **`[PRODUCTIVITY]`**: 90 - 10 points deducted because #12130's functional goals are met, but the PR still needs one small cleanup before becoming merge-ready.
*   **`[IMPACT]`**: 45 - Localized but meaningful AI-ingestion weighting fix: it prevents external/tenant deployments from turning the community multiplier into a boost-all path.
*   **`[COMPLEXITY]`**: 30 - Low complexity: two files, one static registry-derived set, one pure decision helper, and focused tests.
*   **`[EFFORT_PROFILE]`**: Quick Win - High value for external-deployment correctness with a small, contained implementation surface.

After that cleanup, this should be an approval-path PR rather than another architecture cycle.

---

### `@neo-gpt` (APPROVED) reviewed on 2026-05-28T19:13:19Z

# PR Review Follow-Up Summary

**Status:** Approved

**Cycle:** Cycle 2 follow-up / re-review

**Opening:** Re-reviewing the prior `CHANGES_REQUESTED` state against the new head `5aa69a898`, specifically the requested removal of transient ticket anchors from the added IssueIngestor test source.

---

### Strategic-Fit Decision

Per §9 Strategic-Fit Step-Back:
- **Decision**: Approve
- **Rationale**: The only prior blocker was durable test-source metadata hygiene, and the new delta removes exactly that without changing the behavior under review. Current CI and local targeted validation are green.

---

### Prior Review Anchor

*   **PR:** #12144
*   **Target Issue:** #12130
*   **Prior Review Comment ID:** `PRR_kwDODSospM8AAAABBUqoZg` / https://github.com/neomjs/neo/pull/12144#pullrequestreview-4383746150
*   **Author Response Comment ID:** A2A `MESSAGE:efe092c4-b407-4922-8c00-8689407c62e9` (no PR comment; commit `5aa69a898` carries the fixup)
*   **Latest Head SHA:** `5aa69a898018c06cfcfb39bef09e23748e4263f6`

---

### Delta Scope

Summarize what changed since the prior review:

*   **Files changed:** `test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjs`
*   **PR body / close-target changes:** unchanged; `Resolves #12130` remains the valid close target.
*   **Branch freshness / merge state:** `mergeStateStatus: CLEAN`; branch checked out at `5aa69a898`.

---

### Previous Required Actions Audit

*   **Addressed:** Remove the transient `#12130` anchors from the added test source at `IssueIngestor.spec.mjs:213` and `:218` — commit `5aa69a898` changes the block title to `Community-multiplier internal-author resolution` and the suite title to `Neo.ai.daemons.services.IssueIngestor — community-multiplier internal-author set`.

---

### Delta Depth Floor

*   **Documented delta search:** I actively checked the exact diff from `33590a56d` to `5aa69a898`, scanned the changed source for `#12130`, `#12144`, lane, AC, and close-keyword residue, and re-ran the targeted IssueIngestor unit spec; I found no new concerns.

---

### Conditional Audit Delta

**Rhetorical-Drift Delta:** Pass. The changed test comment and suite title now describe behavior only; no transient ticket/source-cycle anchor remains in the added test surface.

### N/A Audits — 📑 📡 🔗
N/A across listed dimensions: the follow-up delta is a test-source wording cleanup only; it does not alter public contracts, MCP tool descriptions, or cross-skill conventions.

---

### Test-Execution & Location Audit

*   **Changed surface class:** test-source wording only
*   **Location check:** pass — unchanged canonical path under `test/playwright/unit/ai/services/ingestion/`
*   **Related verification run:** `git diff --check origin/dev...HEAD` passed; `node --check ai/services/ingestion/IssueIngestor.mjs` passed; `node --check test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjs` passed; `npm run test-unit -- test/playwright/unit/ai/services/ingestion/IssueIngestor.spec.mjs` -> 7 passed.
*   **Findings:** pass

---

### Contract Completeness Audit

*   **Findings:** N/A for this follow-up delta; the functional contract audit from the prior review remains valid and the fixup did not alter implementation behavior.

---

### Metrics Delta

Metrics are unchanged from the prior review unless an explicit delta is listed below.

*   **`[ARCH_ALIGNMENT]`**: 95 -> 100 - The only prior deduction was durable test-source ticket-anchor residue; the delta removes it, and I actively checked source-anchor residue, close-target semantics, and the unchanged registry-derived behavior.
*   **`[CONTENT_COMPLETENESS]`**: 85 -> 100 - The prior 15-point deduction was specifically for `#12130` in the added test block/suite title; commit `5aa69a898` removes those anchors while retaining behavior-focused wording.
*   **`[EXECUTION_QUALITY]`**: 95 -> 100 - The source-hygiene issue is resolved, static checks pass, CI is green, and the targeted unit spec passes locally.
*   **`[PRODUCTIVITY]`**: 90 -> 100 - The prior required action is addressed fully; the PR now satisfies the review path for #12130 without outstanding merge-blocking work.
*   **`[IMPACT]`**: unchanged from prior review at 45 - Localized but meaningful AI-ingestion weighting fix; the follow-up delta does not change impact scope.
*   **`[COMPLEXITY]`**: unchanged from prior review at 30 - Low complexity remains accurate: two-file implementation plus focused test coverage, with a tiny wording-only follow-up.
*   **`[EFFORT_PROFILE]`**: unchanged from prior review at Quick Win - High value for external-deployment correctness with a small, contained implementation surface.

---

### Required Actions

No required actions — eligible for human merge.

---

### A2A Hand-Off

After posting this follow-up review, I will send the review id and URL to the author via A2A for scoped follow-up.

---

