LearnNewsExamplesServices
Frontmatter
id17285
titlemc-server reaches into a host-edge config for its lane-landscape census
stateClosed
labels
bugaiarchitectureagent-os
assigneesneo-opus-ada
createdAtAug 17, 2026, 10:43 AM
updatedAtAug 18, 2026, 11:32 AM
githubUrlhttps://github.com/neomjs/neo/issues/17285
authorneo-opus-ada
commentsCount7
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 18, 2026, 11:32 AM

mc-server reaches into a host-edge config for its lane-landscape census

Closed Backlog/active-chunk-17 bugaiarchitectureagent-os
neo-opus-ada
neo-opus-ada commented on Aug 17, 2026, 10:43 AM

Context

Raised by @tobiu against PR #17280: "why are there 'assigned' and 'unassigned' flags inside the memory core? this is NOT the github workflow server."

The question is about explore_lane_landscape's response schema, but the schema is downstream of a placement decision.

The Problem

explore_lane_landscape is registered on the Memory Core MCP server (ai/mcp/server/memory-core/toolService.mjs:374). To serve it, that file imports:

import from what it is
GraphqlService services/github-workflow/ the GitHub transport
PullRequestHistoryService services/github-workflow/ PR history reads
makeOpenWorkCensusReader services/github-workflow/ the open-issue / open-PR page readers
GitHubWorkflowConfig ../github-workflow/config.mjs the sibling MCP server's config module

The last row is the one that is structurally distinct from the others. A service import is ordinary reuse; reaching into another server's config is not.

Measured, not asserted: grepping every ai/mcp/server/*/toolService.mjs for a sibling-server config import (from '../<server>/config.mjs') returns exactly one hit — this one. It is not an established pattern in the tree. It is used at :111-114 for owner, repo, maxAssigneesPerIssue, maxLabelsPerIssue.

The published response schema then carries the consequence: authorityCoverage.assignedCount / unassignedCount / unassignedIds are pure GitHub issue-assignee facts with no Memory Core content in them. Memory Core's genuine contribution to the landscape is the other half — the Native Edge Graph relation edges.

The crossing is deliberate and documented — which is why this is a design question, not a bug report

toolService.mjs states the split explicitly:

"GitHub repository/query fan-out belongs to the GitHub Workflow child; census traversal bounds belong to the Memory Core child. Reading both at call time preserves reactive overlay/env resolution and fails loud when either provider is not wired."

and frames the tool as "the CURRENT-STATE Bird View, the third self-awareness context slot beside the two historical views."

That reasoning is sound about config resolution — each leaf is read from the child Provider that owns it, at call time, per ADR-0019. It does not address where the tool should live. "This tool is about agent self-awareness" is a claim about purpose; "this tool's data is GitHub's" is a claim about domain. The current placement optimises the first and pays for it in the second.

No ADR governs MCP tool domain ownership. The two decision records touching github-workflow0004-github-content-architecture and 0039-two-plane-sdk-barrel-boundary — settle neither.

The Fork

This ticket does not presume the answer; the tool genuinely joins two domains and reasonable people place it differently.

  1. Move the tool to the GitHub Workflow server. The census, the readers and the config already live there. Cost: it loses cheap access to the Native Edge Graph edges, which is the half that makes it a landscape rather than a list.
  2. Keep it on Memory Core, but consume the sibling as a service rather than importing its config. The self-awareness framing survives; the cross-server config reach — the part with no precedent — does not. Requires deciding how one server asks another for owner/repo/fan-out bounds.
  3. Split the composition. The GitHub Workflow server owns and exposes the open-work census; Memory Core joins it with graph edges. Two tools, each in its own domain, at the cost of a round trip.

Whichever is chosen, the sibling-config import at :33 should not survive as the mechanism — it is the concrete anomaly regardless of where the tool lands.

[!IMPORTANT] Scope narrowed to region A. Everything below the Context was written before the fork was resolved and is RETAINED, not deleted — the reasoning is why the current scope is what it is, and deleting it would send the next reader to re-derive it.

What this ticket is now: the three github-workflow imports serving explore_lane_landscape's open-work census (GitHubWorkflowConfig, makeOpenWorkCensusReader, GraphqlService). Nothing else.

What left scope: PullRequestHistoryService — the fourth import. Its replacement is the credentialed, provider-agnostic conversation source shaped in Discussion #17301, which is architecture rather than a deletion.

The fork below is resolved and it was neither branch — operator direction: conversations reach the container through multi-tenant ingestion using the tenant's own credentials, so no plane-neutral extraction is needed and no capability is surrendered. See the resolution comment for the full disposition.

Acceptance Criteria (current — region A)

  • ai/mcp/server/memory-core/toolService.mjs imports no github-workflow module on the census path: GitHubWorkflowConfig, makeOpenWorkCensusReader and GraphqlService are gone.
  • explore_lane_landscape still resolves rather than throwing — the census source is fail-closed on its injections, so removing the readers would take the tool down along with the violation.
  • The census reports the count as unknown, never zero: an unreachable source yields exhausted: false with a reason, and the landscape withholds its narrative with the cause attached. A stand-in returning an empty page would report proven exhaustion and assert zero open work.
  • The refusal names the boundary (host-edge capability) rather than a generic degradation, and reaches an operator through the walk's reason.
  • Unit coverage includes a positive control proving an empty reader really does report exhausted: true — without it, the refusal assertion proves nothing.

Acceptance Criteria (SUPERSEDED — written against the unresolved fork; retained for provenance)

  • The fork is decided and recorded where a reader of either server will find it — the absence of a rule is why this drifted.
  • ai/mcp/server/memory-core/toolService.mjs no longer imports ../github-workflow/config.mjs; whatever replaces it is a mechanism the tree can have more than one of.
  • The response schema's GitHub-domain fields are served by whichever server the fork assigns the domain to — a consumer should not have to know that assignee facts arrive from the memory server.
  • If the tool stays on Memory Core: the reason is written at the tool boundary as a domain decision, not only as a config-resolution note.

Out of Scope

  • The degraded-count honesty fix (PR #17280 / #17278). It adds no import and creates none of this coupling; it stops an already-published schema from declaring integer for a field that returns null, and that correction travels with the tool wherever it lands.
  • explore_pull_request_history and the other historical views, unless the fork's answer implicates them — PullRequestHistoryService is imported on the same line family and may share the question. Worth checking once the rule exists rather than assuming it does.

Avoided Traps

  • Reading the documented rationale as a decision. The comment explains config resolution correctly and was never a placement ruling; treating it as one is how the anomaly kept its cover.
  • Moving the tool to make the import go away. The import is the symptom that made this visible; the domain question is the thing to settle, and a relocation that leaves the domains still mixed has spent the churn for nothing.

Related

ai/mcp/server/memory-core/toolService.mjs:33, :111-114, :374 · ai/services/github-workflow/openWorkCensusReader.mjs · ai/services/graph/laneLandscapeProjection.mjs · ai/mcp/server/memory-core/openapi.yaml /lanes/landscape · #17278 / PR #17280 (the schema-honesty fix) · #17283 (the credential fork on the same tool) · ADR 0004, ADR 0039

Live latest-open sweep: open queue checked 2026-08-17T08:3xZ; #17283 is the nearest neighbour (same tool, different question — that one is the plane's outbound credential posture, this one is domain ownership). Not a duplicate.

Origin Session ID: 80b326bf-b37a-4efd-8313-1a9eae09e9c4

Retrieval Hint: query_raw_memories("explore_lane_landscape memory-core hosts github census sibling server config import assignedCount")

tobiu referenced in commit 9932ccf - "fix(ai): the lane-landscape census refuses on this plane instead of reaching across it (#17285) (#17324) on Aug 18, 2026, 11:32 AM
tobiu closed this issue on Aug 18, 2026, 11:32 AM