LearnNewsExamplesServices
Frontmatter
id13691
titleWire contentTrust into GitHub sync and KB ingestion (#10476 slice)
stateClosed
labels
enhancementaiarchitecture
assigneesneo-gpt
createdAtJun 21, 2026, 3:38 AM
updatedAtJun 21, 2026, 5:29 AM
githubUrlhttps://github.com/neomjs/neo/issues/13691
authorneo-gpt
commentsCount0
parentIssuenull
subIssues
13703 Add reader-level contentTrust ingestion regression
subIssuesCompleted1
subIssuesTotal1
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJun 21, 2026, 5:29 AM

Wire contentTrust into GitHub sync and KB ingestion (#10476 slice)

Closed v13.1.0/archive-v13-1-0-chunk-4 enhancementaiarchitecture
neo-gpt
neo-gpt commented on Jun 21, 2026, 3:38 AM

Summary

Wire the existing contentTrust sanitizer/projection substrate into the GitHub workflow write and ingestion boundaries, so untrusted external GitHub-authored content is defanged before it is persisted into resources/content/* and before KB / graph ingestion turns that content into retrievable context.

Release Classification

Post-release hardening / organism self-defense. This is not a v13 release blocker, but it closes a remaining slice of #10476 now that the pure helper and read-path projection slices have landed.

Context

Parent #10476 defined three anti-astroturfing protections:

  • read-boundary URL defanging for GitHub conversations
  • an explicit external URL traversal prohibition for agents
  • P7 / Memory Core contextual sanitization so Neo does not persist untrusted marketing or watering-hole links into its own memory substrate

The first code slice is already done:

  • #13026 closed the pure author-tier classifier and astroturfSanitizer helper.
  • #13046 closed contentTrust projection for conversation read paths.

The remaining code gap is the write / ingestion boundary. The local GitHub syncers still serialize external GitHub-authored Markdown verbatim into repo content files, and the downstream KB / graph readers consume those files as-is.

Verify-Before-Assert Evidence

To assert the write-boundary gap, I checked the current source and let the code shape determine this ticket:

  • ai/services/github-workflow/sync/IssueSyncer.mjs #formatIssueMarkdown() writes issue.body and IssueComment event.body directly into the local issue Markdown.
  • ai/services/github-workflow/sync/PullRequestSyncer.mjs writes PR body, comments, and review bodies directly into local PR Markdown.
  • ai/services/github-workflow/sync/DiscussionSyncer.mjs writes discussion body, comments, and replies directly into local discussion Markdown.
  • ai/services/knowledge-base/source/TicketSource.mjs chunks the full local issue Markdown content for KB indexing.
  • ai/services/ingestion/IssueIngestor.mjs embeds open issue and discussion title/body content into the graph collection.
  • Duplicate sweep before creation: live open issue search for contentTrust ingestion sanitizer astroturf 10476, P7 contentTrust, and External URL Traversal / QUARANTINED_URL returned only parent #10476.

Problem

get_conversation callers now see a defanged view, but the periodic GitHub sync path can still write raw external URLs and product-seeding prose into resources/content/issues, resources/content/pulls, and resources/content/discussions.

Once written, that content is no longer just a remote GitHub payload. It becomes local source material for KB chunks and graph embeddings, which recreates the risk #10476 was meant to remove: Neo can preserve and later surface untrusted external links or astroturf payloads as internal context.

Acceptance Criteria

  • Issue sync applies the same trust policy as projectConversationTrust before serializing root issue bodies and timeline comments to local Markdown. External or unclassified authors get URL defanging and configured product-name redaction; trusted authors remain byte-identical.
  • Pull request and discussion sync cover the same authored-node classes they serialize today: PR bodies, PR comments, PR review bodies, discussion bodies, discussion comments, and discussion replies.
  • The persisted Markdown carries enough machine-readable trust metadata for downstream consumers to distinguish contentTrust-projected content from legacy unsanitized content. A frontmatter summary is acceptable; do not require every historical file to be rewritten in one shot.
  • KB and graph ingestion consume sanitized local content for newly synced external-authored payloads. Tests must prove raw external URLs do not enter TicketSource chunks or IssueIngestor embedding documents for the covered untrusted-author cases.
  • Product-name denylist wiring is config-resolved with an empty safe default; do not hard-code vendor/product strings in syncers or ingestion code.
  • Existing conversation read-path behavior from #13046 remains unchanged.

Test Plan

  • Add focused unit coverage around the sync formatting/projection helper layer for issue, PR, and discussion authored-node shapes.
  • Add one downstream ingestion regression that writes a sanitized external issue/discussion fixture and proves TicketSource / IssueIngestor output contains [QUARANTINED_URL: domain] and not the raw URL.
  • Run the narrow affected Playwright unit suites, not the default Playwright command.

Out of Scope

  • Deleting, hiding, or editing GitHub comments remotely.
  • Rewriting all historical resources/content/* files in this slice.
  • The AGENTS.md external traversal prohibition from #10476, unless a separate turn-memory pre-flight explicitly scopes that substrate edit.

Related

tobiu referenced in commit cff0dfa - "feat(ai): wire contentTrust into sync ingestion (#13691) (#13693)" on Jun 21, 2026, 5:29 AM
tobiu closed this issue on Jun 21, 2026, 5:29 AM