The issues subsystem is the primary feedback channel for iterative Neotoma improvement based on agentic usage. v0.12.0 hardens the reporter provenance contract, formalizes operator-mirror flows, and adds bulk operator tools for triage.
Use the issues surface when:
An agent hits friction during a Neotoma operation (failing tool call, opaque error, missing surface, doc gap) and the harness
issues.reporting_modeallows filing.A per-turn self-audit produces a file-worthy finding (schema-projection drift, ambiguous identity rule, recurring product weakness) and the agent should file or escalate.
A human operator wants to triage many issues at once (close, soft-remove) from the Inspector or scripts.
MCP tools
submit_issue({ title, body, labels?, visibility?, reporter_git_sha?, reporter_git_ref?, reporter_channel?, reporter_app_version?, reporter_ci_run_id?, reporter_patch_source_id? })— creates a localissueentity and optional GitHub / operator mirror pervisibility. Required (v0.12+): at least one ofreporter_git_shaorreporter_app_version. Rejection envelope returnserror_code: "ERR_REPORTER_ENVIRONMENT_REQUIRED"plusdetails.acceptable_field_groups: [["reporter_git_sha"], ["reporter_app_version"]]so callers can retry with the right alternative.add_issue_message({ entity_id, body, guest_access_token? })— appends a thread message. On public threads, missing bothreporter_git_shaandreporter_app_versionemits a server-side warning (the message still persists).get_issue_status({ entity_id, skip_sync?, guest_access_token? })— snapshot + messages, with operator read-through and GitHub refresh when mirrored.sync_issues({ since?, state?, labels? })— bulk pull from GitHub into the local instance.bulk_close_issues({ entity_ids: string[], reason? })— closes multipleissueentities in one call.bulk_remove_issues({ entity_ids: string[], reason? })— soft-deletes multipleissueentities viadeleteEntityobservations; restore throughrestore_entity.
HTTP routes
POST /issues/submit—submitIssue(MCPsubmit_issueparity).POST /issues/add_message—addIssueMessage(MCPadd_issue_messageparity).POST /issues/status—getIssueStatus.POST /issues/sync—syncIssuesFromGitHub.POST /issues/bulk_close—bulkCloseIssues.POST /issues/bulk_remove—bulkRemoveIssues.
Each /issues/* route is also mounted under /api/issues/* for same-origin proxies.
Reporter provenance contract
submit_issue (and the add_issue_message soft-warn path) require enough environment to reconstruct what an agent saw at filing time. The required fields:
Breaking change in v0.12.0: Pre-v0.12 submit_issue calls without any of reporter_git_sha / reporter_app_version were accepted. They now reject with ERR_REPORTER_ENVIRONMENT_REQUIRED. See the contract fixture at tests/contract/legacy_payloads/v0.12.x/issues_submit_without_reporter_env.
Reporting modes
issues.reporting_mode (overridable via NEOTOMA_ISSUES_REPORTING_MODE) gates whether an agent files autonomously:
consent(default) — obtain explicit user approval before eachsubmit_issue.proactive— file immediately without asking.off— only file when the user explicitly requests it.
Persisted via neotoma issues config --mode <choice>.
GitHub mirror + guest token flow
When visibility: "public" and an issues.target_url is configured, submit_issue writes the local row first and then mirrors to the configured operator instance. The response may include a guest_access_token scoped to the issue thread so subsequent add_issue_message / get_issue_status calls can read through to the operator copy without re-authenticating. The token persists on the local issue snapshot and is treated as a credential.
Sensitive content is redacted client-side before mirroring: emails, phone numbers, API tokens, UUIDs, and home-directory path fragments are replaced with <LABEL:hash> placeholders so the public GitHub mirror stays safe.
Example
File a public issue from an agent run:
neotoma issues create \
--title "store rejects flat conversation rows" \
--body "Repro: store with entity_type: conversation, no target_id…" \
--visibility public \
--labels mcp,store
Triage a batch of duplicates via the Inspector or scripts:
curl -X POST http://localhost:3080/issues/bulk_close \
-H "Authorization: Bearer $NEOTOMA_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"entity_ids":["ent_…1","ent_…2","ent_…3"],"reason":"duplicate of #42"}'
Full reference
docs/subsystems/issues.md covers the reporter-provenance contract end to end, the sync_issues_from_github.ts and submit_issue.ts services, the redaction backstop in the entity-submission flow, and the recommended process-issues operator skill.
See security hardening, API reference, and MCP reference.