원클릭으로
audit
audit
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
audit
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Canonical turn protocol for agents writing to Neotoma. Bounded retrieval on the user message, store user message PART_OF conversation with REFERS_TO edges to retrieved entities, store assistant reply the same way. Use this skill in any agent loop that should persist conversation state to Neotoma.
Session-end audit. Files remaining work as task entities and lists them as bullets, verifies all data intended for Neotoma storage from this session is actually stored, and persists what's missing. Invoke at the natural close of a working session, before context is lost.
Mid-session status report. Summarizes what's been achieved so far this session and what work remains, in succinct qualitative prose and bullets with only light technical detail. Read-only — stores nothing, files nothing. Invoke any time to take stock without closing the session.
Close a Neotoma working session — store the assistant reply and a session-close summary.
Retrieve the bounded context for the current session — recent interactions and relevant entities.
Begin a Neotoma working session — open a conversation and record the opening interaction.
| name | audit |
| description | audit |
Run a comprehensive read-only audit of the user's Neotoma database, surfacing graph-health issues by category and severity. Each finding includes a structured remediation pointing at an existing Neotoma tool — this skill never mutates state.
Neotoma has strong write-path discipline (idempotency, schema-first, immutability, deterministic IDs) but no holistic read-path "is my graph healthy?" tool. /audit fills that gap.
In scope: detection of inconsistencies, drift, and quality issues across entities, observations, relationships, schemas, and raw_fragments.
Out of scope: mutation of any kind. Repair is performed by delegating to other tools or skills (merge_entities, correct, split_entity, delete_entity, register_schema, update_schema_incremental). The user opts into each repair individually after reviewing findings.
/audit — Run all deterministic checks. No LLM cost. Default./audit --deep — Run deterministic + LLM-assisted checks. Requires an LLM API key (read from NEOTOMA_AUDIT_LLM_KEY env var or passed via --key=…). Models default to a cheap small model; override with --model=…./audit --scope=<entity_type|relationship|schema|fragments> — Restrict checks to one category./audit --since=<ISO date> — Restrict to entities/observations created or updated since the date./audit --user=<user_id> — Audit a specific user's records (defaults to authenticated user)./audit --dup-min-count=<N> — Minimum entity count for a type to be auto-scanned for potential duplicates beyond the always-checked contact/person/organization set (default 100)./audit --format=<table|json|markdown> — Output shape; table default for interactive, json for piping into repair tooling.mcpsrv_neotoma).--deep: LLM API key configured (NEOTOMA_AUDIT_LLM_KEY or --key=…).get_session_identity if no --user flag).--deep.Test-fixture filter (applies to all checks): skip any entity_type matching ^(test_type_|test_activate_|cross_layer_schema_) unless --include-test-fixtures is passed. These are integration-test scaffolding, not real registry entries.
Run all of the following. Each check produces zero or more Finding records. Each Finding has:
{
check_id: "DUP_TYPE_ALIASES",
severity: "high" | "medium" | "low" | "info",
category: "entity_type" | "entity" | "observation" | "relationship" | "schema" | "fragments" | "naming",
entity_ids: ["ent_..."], // affected entities
summary: "Short human-readable summary",
detail: "Longer explanation with counts/examples",
remediation: {
tool: "merge_entities" | "correct" | "register_schema" | …,
args_hint: { ... }, // suggested args; user reviews before applying
description: "Merge ent_A into ent_B as the canonical record"
}
}
| ID | Category | What it detects | Remediation tool | Severity |
|---|---|---|---|---|
DUP_ENTITIES_BY_RESOLVER | entity | Entities flagged by list_potential_duplicates (server-side resolver matches). | merge_entities | high |
DUP_TYPE_ALIASES | entity_type | Two registered entity types whose aliases arrays overlap or whose canonical names are synonyms (e.g. vendor vs merchant). | register_schema (alias one to the other) | medium |
PLURAL_TYPE_NAMES | entity_type | Registered entity_type is plural and not in the irregular allowlist (per schema_agnostic_design_rules.md). | register_schema (rename to singular + alias) | medium |
ORPHAN_ENTITIES | entity | Entities with no PART_OF, REFERS_TO, or other inbound/outbound relationships, and no observations after the first one. | delete_entity or merge_entities | low |
CYCLE_HIERARCHY | relationship | Cycles in hierarchical relationship types (PART_OF, DEPENDS_ON) — explicitly forbidden by relationships.md. | Manual review + delete_relationship | high |
UNTYPED_OR_INVALID_EDGE | relationship | Relationships whose relationship_type is not in the registered enum. | delete_relationship + recreate with valid type | high |
ZERO_OBSERVATION_ENTITY | entity | Entities with snapshot but observation_count == 0 (resolver edge case — should not happen post-bootstrap). | delete_entity after manual verification | medium |
MISSING_SOURCE_PROVENANCE | observation | Observations with null source_id AND null interpretation_id (provenance broken). | None (immutable) — investigate ingestion path | high |
UNKNOWN_FIELDS_COUNT_HIGH | fragments | Interpretations with unknown_fields_count > 0 for an entity_type that has a registered schema (means schema is missing declared fields). | update_schema_incremental to add fields | medium |
FRAGMENT_DUMP | fragments | Entities where raw_fragments count exceeds the 95th percentile and the entity has fewer than N declared snapshot fields (signals unstructured dumping). | register_schema to declare common fragment_keys | medium |
FRAGMENT_KEY_HIGH_FREQ | fragments | fragment_key values whose frequency_count exceeds threshold T for a given entity_type — strong promotion candidates. | update_schema_incremental to promote field | info |
NON_READABLE_CANONICAL | naming | canonical_name matches a UUID pattern, hex hash, empty string, or pure punctuation/whitespace. | correct (provide identity-bearing field) | medium |
MISSING_REQUIRED_FIELD | schema | Entity snapshot missing a field declared required on its registered schema. | correct to add field | high |
RELATIONSHIP_DANGLING | relationship | Relationship references entity_id that does not exist or is soft-deleted (merged_to_entity_id set). | delete_relationship or update to canonical target | high |
SCHEMA_NO_CANONICAL_FIELDS | schema | Registered schema has empty canonical_name_fields — every store of that type creates a new row. | register_schema to declare canonical fields | high |
INTERPRETATION_HEARTBEAT_STALE | observation | Interpretation rows with started_at set, completed_at null, and heartbeat_at older than threshold. | Investigate — possibly stuck interpretation | medium |
ENV_CONTAMINATION | entity | Entity snapshot fields contain values matching known dev/test/staging environment markers (e.g. localhost, 127.0.0.1, dev., .local, test-, -dev, -staging, Kubernetes cluster DNS). Signals records imported from a non-production environment into a production database, or vice versa. | correct (update field value) or delete_entity | varies |
DUP_ENTITIES_BY_RESOLVER — Always run for the high-cardinality entity types where duplicates accumulate fastest: contact, person, organization. Call list_potential_duplicates once per type (passing entity_type). When --scope=entity or --scope=entity_type is not set, also include any other registered type whose entity count exceeds the per-run high-cardinality threshold (default 100; override with --dup-min-count=N). Skip types with fewer than 2 entities. For each returned pair, emit one Finding: entity_ids is [entity_a.id, entity_b.id]; summary names both canonical_name values; detail includes score, matched_fields, and entity_type; populate args_hint with { source_entity_id: entity_b.id, target_entity_id: entity_a.id, strategy: "prefer_more_recent" | "prefer_more_observations" } (merge the lower-ranked pair member into the higher-ranked one — entity_a ranks first by score). Severity is high when score >= 0.95, otherwise medium. Detection only — never call merge_entities from a check.DUP_TYPE_ALIASES — Call list_entity_types; build a graph of (canonical_name → aliases). Edges where two distinct canonical names appear in each other's alias sets, OR where alias arrays intersect, become findings. Levenshtein < 2 on short names emits as low severity.PLURAL_TYPE_NAMES — list_entity_types and apply the same singularization rule used by validateSchemaDefinition (load from NEOTOMA_ALLOWED_PLURAL_TYPES env var or fall back to a hardcoded allowlist: news, data, analytics, status, series, species). Extend the in-skill allowlist with confirmed legacy import artifacts where the plural name is semantically intentional (one entity representing many items imported in bulk): reads (IndieWeb URL export, 2017-2018), sections (website section config blob), songs, talking_points, crypto_transactions. Emit these as info severity with a note that they are legacy import artifacts, not schema violations — do not co-emit DUP_TYPE_ALIASES for them. Validated against the live registry on 2026-05-16: surfaces places, plans, reads, songs, sections, crypto_transactions, talking_points, meeting_notes (before legacy allowlist). After applying legacy allowlist, actionable findings are places, plans, meeting_notes only. Three of those (places/plans/meeting_notes) also have singular siblings already registered — co-emit DUP_TYPE_ALIASES for those, ranked as the highest-priority repair candidates since rows are split across both types.ORPHAN_ENTITIES — retrieve_entities per entity_type; for each entity, list_relationships with the entity as source or target. Empty → orphan.CYCLE_HIERARCHY — list_relationships filtered by PART_OF/DEPENDS_ON; DFS cycle detection over the directed graph.UNTYPED_OR_INVALID_EDGE — list_relationships; compare each relationship_type against the enum from the relationship schema (PART_OF, CORRECTS, REFERS_TO, SETTLES, DUPLICATE_OF, DEPENDS_ON, SUPERSEDES, EMBEDS, plus domain-specific types from registry).ZERO_OBSERVATION_ENTITY — retrieve_entity_snapshot; check observation_count == 0.MISSING_SOURCE_PROVENANCE — list_observations per entity (or sample); flag rows where both source_id and interpretation_id are null. Note: immutable — finding is informational unless ingestion is still actively producing them.UNKNOWN_FIELDS_COUNT_HIGH — Aggregate unknown_fields_count from interpretations table per entity_type. Anything > 0 for a type with a registered schema is a finding; bucket by fragment_key frequency.FRAGMENT_DUMP — Compute per-entity_type fragment counts (raw_fragments joined to observations). Compare each entity to the per-type 95th percentile. High count + low snapshot density = finding.FRAGMENT_KEY_HIGH_FREQ — Group raw_fragments by (entity_type, fragment_key), sum frequency_count. Threshold defaults: keys appearing on >25% of entities of that type.NON_READABLE_CANONICAL — Regex against canonical_name: UUID (^[0-9a-f]{8}-…), hex (^[0-9a-f]{16,}$), empty, whitespace-only, single-character.MISSING_REQUIRED_FIELD — Load schema definitions via list_entity_types; for each entity, diff snapshot keys against required_fields.RELATIONSHIP_DANGLING — list_relationships; for each row, verify source_entity_id and target_entity_id resolve via retrieve_entity_snapshot and are not soft-deleted.SCHEMA_NO_CANONICAL_FIELDS — list_entity_types; flag any with empty canonical_name_fields.INTERPRETATION_HEARTBEAT_STALE — Query interpretation rows; threshold defaults to 15 minutes.ENV_CONTAMINATION — For each entity, call retrieve_entity_snapshot and pass the resulting field map to scanSnapshotForContamination() (implemented in src/services/env_contamination_audit.ts). The function tests every string-valued field against ENV_CONTAMINATION_INDICATORS (loopback IPs, localhost, Kubernetes cluster-internal DNS, .local mDNS, dev./staging. sub-domains, test_user/test-user patterns, test- prefixes, -dev/-staging suffixes, nil UUID, Docker private IP ranges). Structural metadata fields (entity_id, created_at, updated_at, observation_count, user_id, _migration_run_id) are skipped to avoid false positives. The severity of each finding is the highest severity among matched indicators (high > medium > low). For efficiency, sample up to the 500 most recently updated entities per audit run unless --scope=entity is active. Populate entity_ids with the IDs of flagged entities and detail with per-entity summaries of the matched fields and indicators. args_hint for the remediation: { entity_id, fields_to_correct: ["<field>", ...] }.--deep)Run these only if the API key is present. Each check batches entities and asks the LLM for structured JSON output. Key never leaves the local process.
| ID | What it detects | Remediation tool |
|---|---|---|
LLM_CANONICAL_READABILITY | canonical_name is technically valid (passes deterministic regex) but not human-readable (e.g., payment-3-2, unnamed-vendor-7). | correct with suggested name |
LLM_ENTITY_TYPE_MISCLASS | Content semantically a transaction but stored as note/message/raw. LLM compares snapshot+observations to type registry. | correct with entity_type change |
LLM_SEMANTICALLY_EQUIVALENT_TYPES | Two entity_types not alias-linked but semantically equivalent (vendor vs merchant vs payee). | register_schema (alias) |
LLM_FRAGMENT_PROMOTION | Raw fragments that are stable, structured, and high-value enough to promote to declared schema fields. | update_schema_incremental with suggested field shape |
LLM_RELATIONSHIP_SUGGESTION | Pairs of entities whose observation text strongly implies a relationship that isn't declared. | create_relationship with suggested type |
LLM_DUPLICATE_BY_CONTENT | Entities that escaped resolver-based duplicate detection but have semantically equivalent content (different spellings, abbreviations). | merge_entities |
Output sections, in this order:
Found 47 issues (3 high, 12 medium, 32 low/info).DUP_ENTITIES_BY_RESOLVER findings, broken out from the generic severity lists because duplicate accumulation is the most common silent graph-health failure. Group by entity_type; within each type list the candidate pairs ranked by score (truncate to 10 with (+N more) unless --verbose). Each row shows both canonical_name values, the score, the matched_fields, and the merge hint. End the section with the always-checked types that returned zero candidates so the operator sees the check ran (contact: no candidates). If no pairs were found across any type, print Potential duplicates: none detected. Remediation is merge_entities only — emit the hint, never merge.check_id, summary, affected entity ids (truncated to 5 with (+N more)), and remediation one-liner.--verbose.1. Merge 7 duplicate pairs (merge_entities), 2. Rename 2 plural entity types (register_schema), etc. The user picks individually; no batch auto-apply.For each remediation the user authorizes:
remediation.tool.args_hint filled in.--yes to apply all of one category).Never auto-apply. Repair is always per-finding (or per-category with --yes), never per-audit-run.
Finding for every issue surfaced; no silent skips.remediation.tool pointing at an existing Neotoma tool (or null with rationale if no automated repair is possible).--deep and a key is present.$NEOTOMA_AUDIT_COST_CAP (default $1).--user scope and refuse to audit other users' data without explicit authorization (get_session_identity).entity_type: audit_run, with findings_count, started_at, completed_at, mode, scope) for historical comparison.merge_entities, correct, delete_entity, etc., without explicit user confirmation per finding.info finding with the tool response./audit (deterministic only)Auditing Neotoma database for user mark (≈8,420 entities)
Running 15 deterministic checks…
Summary: 47 findings (3 high, 12 medium, 32 low/info)
Potential duplicates (DUP_ENTITIES_BY_RESOLVER) — 7 candidate pairs
contact (5 pairs)
"Jane Doe" ↔ "Jane R. Doe" score 0.97 [canonical_name, email]
→ merge_entities(source=ent_bbb…, target=ent_aaa…, strategy=prefer_more_observations)
"Acme, Inc." ↔ "Acme Inc" score 0.93 [canonical_name]
→ merge_entities(source=ent_ddd…, target=ent_ccc…, strategy=prefer_more_recent)
… (+3 more, run --verbose)
organization (2 pairs)
"Vercel" ↔ "Vercel Inc." score 0.91 [canonical_name]
→ merge_entities(source=ent_fff…, target=ent_eee…, strategy=prefer_more_recent)
person: no candidates
HIGH (2)
[CYCLE_HIERARCHY] 1 cycle detected in PART_OF graph
→ ent_ccc → ent_ddd → ent_eee → ent_ccc
→ Remediation: manual review + delete_relationship
[SCHEMA_NO_CANONICAL_FIELDS] entity_type "note" has no canonical_name_fields
→ Every store creates a new row; deduplication impossible
→ Remediation: register_schema with canonical fields
MEDIUM (12) — collapsed, run with --verbose
LOW/INFO (32) — collapsed, run with --verbose
Repair menu:
1. Merge 7 potential duplicate pairs (merge_entities, review each) [y/n]
2. Resolve cycle in PART_OF graph (manual) [skip]
3. Add canonical_name_fields to "note" schema [y/n]
Run --verbose for medium/low remediations.
/audit --deep --scope=namingAuditing naming category only (deep mode, model gpt-4o-mini)
Estimated LLM cost: $0.18 — proceed? [y]
Running 1 deterministic check (NON_READABLE_CANONICAL)…
Running 1 LLM check (LLM_CANONICAL_READABILITY)…
Summary: 23 findings (0 high, 18 medium, 5 info)
MEDIUM (18)
[NON_READABLE_CANONICAL] 12 entities with UUID-shaped canonical_name
[LLM_CANONICAL_READABILITY] 6 entities with technically-valid but unreadable names
→ ent_fff "payment-3-2" → suggested "Acme Corp - Invoice #3 - Feb 2026"
→ ent_ggg "vendor-unnamed-7" → suggested "Cloudflare"
→ … (4 more)
→ Remediation: correct (with LLM-suggested names)
Repair menu:
1. Apply 12 deterministic name corrections (correct, manual) [y/n]
2. Apply 6 LLM-suggested name corrections (correct, review each) [y/n]
docs/foundation/schema_agnostic_design_rules.md — singular type names, schema-driven behavior.docs/subsystems/observation_architecture.md — raw_fragments lifecycle.docs/subsystems/relationships.md — typed edges, cycle prohibition.docs/subsystems/schema.md — raw_fragments, interpretations schema.docs/subsystems/entity_merge.md — merge semantics.docs/foundation/entity_resolution.md — resolver path and identity rules.This skill is the prototype shape. Once the check set stabilizes (≥2 weeks of use, deterministic checks ratified) promote to a first-class CLI command:
neotoma audit [--deep] [--scope=…] [--since=…] [--format=…]docs/architecture/openapi_contract_flow.md.docs/feature_units/in_progress/FU-…audit/.