{
  "markdown": "# CharacterCheck\n\n**Agents start here → [SKILL.md](SKILL.md)** — when to call this, worked examples, MUST/MUST NOTs. Family contract: [FAMILY.md](https://github.com/chaoz23/srdcheck/blob/main/FAMILY.md).\n\nCharacterCheck is an experimental, read-only compiler for selected D&D Beyond\ncharacter-sheet fields. It turns a public share, saved character-service JSON,\nor versioned snapshot into deterministic mechanical context with provenance and\nnamed findings.\n\n> **Current release: 0.7.0.** Pin `charactercheck==0.7.0` when installation\n> must be reproducible. Verify the installed package version and each field's\n> trust state; a version string alone is not proof that a value is supported.\n\nIt is not a complete or rules-authoritative character validator. Its output is\nnot encounter, world, or session state and does not prove that an action is\nlegal. Unknown upstream fields and unsupported restrictions can affect results;\nread the trust and finding data before using a value.\n\n## Run 0.7.0 offline\n\nPython 3.9 or newer is required. The runtime has no third-party dependencies.\n\n```console\ngit clone https://github.com/chaoz23/charactercheck\ncd charactercheck\npython3 -m charactercheck selftest\npython3 -m charactercheck derive examples/sample-character.json --brief\n```\n\nThe bundled example is project-authored synthetic data. The smoke test checks\nthis checkout and selected derivations; it is not evidence of complete D&D\nrules correctness.\n\nInstall the published package into a virtual environment for the console entry\npoints:\n\n```console\npython3 -m venv .venv\n.venv/bin/python -m pip install charactercheck==0.7.0\n.venv/bin/charactercheck derive examples/sample-character.json --brief\n```\n\nThe direct `python3 -m charactercheck` path from a clone has no runtime\ndependency install. An editable pip install may bootstrap the `setuptools`\nbuild frontend if the environment does not already provide it. See\n[MIGRATING.md](https://github.com/chaoz23/charactercheck/blob/main/MIGRATING.md)\nbefore comparing 0.7.0 output or MCP surfaces with 0.6.x.\n\nFor an explicitly public character:\n\n```console\npython3 -m charactercheck derive https://www.dndbeyond.com/characters/<id>\n```\n\nPublic sharing permits retrieval at that moment; it does not establish consent\nfor publication, recurring tests, persistence, model training, or reuse of\npersona and third-party narrative content.\n\nCharacterCheck requests no credentials. For a private sheet, use an authorized\nsaved character-service JSON file through the local CLI or library; do not pass\ncookies, tokens, or session data.\n\n## Product and authority boundary\n\nCharacterCheck currently supports mechanical, read-only preparation for a\nhuman or AI-controlled DM, PC, or NPC. It observes a character source and emits\ncontext or reconciliation candidates. It exposes no mutation tools and does not\nbecome the authority for HP, conditions, concentration, expended resources,\nequipment declarations, DM rulings, encounter state, or world state.\n\nThe human principal remains responsible for access and use. An AI actor must\nnot infer permission, role, or visibility from sheet contents or a tool\nargument. Where a value is marked `confirm`, `unsupported`, `unknown`, or\n`invalid`, the agent should ask the named human authority or decline to use the\nvalue. Manual play without CharacterCheck remains the fallback.\n\n## Ecosystem and calculation boundary\n\nCharacterCheck is being developed as a versioned **D&D Beyond-to-agent\ncompiler and conformance facade**, not a new complete 5e rules engine. The\nstrict source/snapshot/privacy/trust layer remains local. Adapter vocabulary is\nseeded from a pinned offline DDB config registry that was independently\nobserved and cross-checked against the MIT-licensed DDB Importer fallback. The\nproduction runtime never fetches that config endpoint. The distributed\nregistry is a purpose-limited allowlist for current evaluator branches—not a\ncopy of DDB's full configuration or third-party catalog vocabulary.\n\nRegistry membership establishes only that an upstream ID/name was observed;\nit does not prove that CharacterCheck implements its mechanics. Bounded unknown\nIDs are retained as opaque adapter evidence, fixed semantic-gap codes prevent\ndefault calculations, and unsafe formula/property prose is omitted. Current\nweapon proficiency/hand state and unimplemented property semantics therefore\nremain explicitly unsupported even when a provisional attack line can be\nrendered.\n\nThe next mechanics work is a differential conformance spike using exact SRD\nrevisions, narrow MIT-compatible DDB Importer evidence, and Foundry dnd5e as a\nversioned model/behavior oracle. DDB/displayed and third-party calculated values\nare attributed claims, not unquestionable truth; disagreement must become a\nnon-trusted conflict. See\n[ECOSYSTEM_CONFORMANCE.md](https://github.com/chaoz23/charactercheck/blob/main/ECOSYSTEM_CONFORMANCE.md)\n[SOURCE_FIELD_ROUTING.md](https://github.com/chaoz23/charactercheck/blob/main/SOURCE_FIELD_ROUTING.md),\nand [THIRD_PARTY_NOTICES.md](https://github.com/chaoz23/charactercheck/blob/main/THIRD_PARTY_NOTICES.md).\n\nThe observed DDB service/config surfaces remain undocumented and unsupported\nas public APIs. Their technical availability does not grant commercial access,\ncontent rights, stability, or consent. A commercial build needs a licensed or\ncounsel-approved input/update path and should prefer an authorized user/host\nexport until then.\n\n## Inputs\n\nThe input parser accepts exactly:\n\n- a positive 1–20 digit character ID with no leading zero;\n- an HTTPS URL on `dndbeyond.com` or `www.dndbeyond.com` whose path is exactly\n  `/characters/<id>`;\n- a direct local regular JSON file, when the calling surface permits files; or\n- a `CharacterSnapshotV1` JSON file.\n\nLocal symbolic links are rejected. Missing paths are not reinterpreted as IDs.\nRaw local character files and HTTP responses are bounded to 8 MiB; a local\nCharacterSnapshotV1 envelope may be up to 16 MiB so a maximum-size filtered\nsource can round-trip with its metadata. JSON also has depth, node, string,\ncollection, inventory, modifier, and container-traversal limits.\n\n## Commands\n\n| Command | Current contract |\n|---|---|\n| `derive <ref>` | Selected derived fields, provenance, findings, and trust routing |\n| `derive <ref> --brief` | Deterministic chat-sized summary; still subject to findings |\n| `derive <ref> --table-evaluation` | Value-free, deterministic, self-attested `table.evaluation/1.0` assessment envelope |\n| `stance <ref>` | Envelope containing stance, canonical assessment, trust, fields, and observation metadata |\n| `report <ref>` | Canonical field/trust assessments plus findings and identified feature names |\n| `qa <ref> [--full]` | 100-question **Coverage Inventory** pairing each answer with a closed field state; not a validity score |\n| `seatpack <ref>` | Privacy-minimized read-only character context |\n| `intake <ref>` | Pre-session context plus questions and authority boundaries |\n| `quiz <ref>` | Settlement and sheet-specific finding questions; non-trusted values never become answer keys |\n| `snapshot <ref>` | Export a versioned, integrity-checked observation |\n| `diff <ref> --baseline snapshot.json` | Classify supported changes; mark omitted-source comparisons indeterminate |\n| `doctor [ref] [--json]` | Diagnose runtime/network/source access without echoing the ref |\n| `selftest` | Offline installation and selected-derivation smoke test |\n\n`--pipe` reads refs from standard input. `charactercheck --schema` emits the\nmachine-readable CLI contract.\n\n`--table-evaluation` is also accepted by `report`. It projects canonical field\nstates and source/policy digests, never character values. Trusted fields count\nas evaluated; unsupported, unknown, invalid, and non-authority confirmation\nstates fail closed. Mutable player-authority fields are outside the evaluator's\nscope and become advisories requiring player/session-host reconciliation. The\nenvelope is always `self_attested`; it is not proof of source identity,\nfreshness, table role, encounter state, or action legality.\n\n### Question catalog\n\nThe Coverage Inventory pairs each of its 100 answers with the corresponding\nhuman-readable D&D 2024 character-sheet lookup question. Structured rows carry\n`number`, `field`, `question`, `state`, `value`, and `content_trust`. The\nquestion catalog is an organizational contract, not an assertion that the\nsupplied JSON Schema's value types or the complete 2024 rules are implemented.\nAn answer remains unusable when its row is `unsupported`, `unknown`, or\n`invalid`; mutable `confirm` rows remain player/session-host authority.\n\nThe settlement `quiz` also includes each sheet-specific lint question exactly\nonce. Those prompts never receive an expected answer when their affected\nfamily is not trusted. Account identity and roleplay/persona questions remain\nprivacy-omitted from default output.\n\n### Snapshots and diff\n\nCapture a baseline and compare a later observation:\n\n```console\npython3 -m charactercheck snapshot examples/sample-character.json > baseline.json\npython3 -m charactercheck diff examples/sample-character.json --baseline baseline.json\n```\n\n`CharacterSnapshotV1` records the adapter, source ID, source-schema fingerprint\n(which commits to the pinned adapter registry and semantic-gap contract),\nobservation time, engine/rules profile, privacy classification, hashes,\ncoverage, and a snapshot ID. Canonical derived metadata also exposes the\nadapter-registry fingerprint directly. Account identifiers and linked images\nare always removed.\nPersona is excluded by default. Snapshots do not migrate silently; unsupported\nversions fail with `snapshot_schema`. Observation times use canonical UTC\nRFC 3339 (`...Z`) syntax; they describe caller/source observation order, not a\ntrusted timestamp or proof of freshness.\n\nThe hash meanings are intentionally distinct:\n\n- `source.normalized_data_hash` is the canonical hash of the default\n  privacy-filtered mechanical character. Projections use it as\n  `source_revision`; it deliberately excludes persona and every omitted field.\n- `source.snapshot_character_hash` hashes the exact filtered `character` stored\n  in the snapshot, so an explicitly persona-inclusive snapshot can differ from\n  the default mechanical revision.\n- `meta.snapshot_id` covers the complete snapshot envelope except for the ID\n  field itself, including observation metadata, privacy declarations, coverage,\n  and the stored character.\n\nThese hashes are deterministic integrity/content identifiers, not signatures,\nattestations, or proof of source authenticity. A party able to edit a snapshot\ncan recompute unkeyed hashes. Keep baselines in a trusted store and let an\nauthenticated session host bind them to principals and audit history.\n\nSnapshot filtering is closed at the top level and inside supported nested\nobjects. `source.coverage` contains three booleans:\n`unclassified_top_level_omitted`, `unclassified_nested_omitted`, and\n`semantic_values_omitted`, plus a sorted `scoped_mechanical_omissions` list of\ncanonical family names. It never carries omitted source names or values.\nReviewed display/provenance omissions are trust-neutral; reviewed mechanical\nomissions add `source:scoped-fields-omitted` and route only listed families to\n`unsupported`. The first two booleans have unknown mechanical scope; derive adds\nthe static `source:unclassified-fields-omitted` finding and routes every\nmechanical family to `unknown`. The third records unsafe semantic text removed\nafter a fixed, field-scoped `_semanticGaps` code was retained; the\nitem-semantic ledger routes the affected family to `unsupported` without\ncopying the text. The same coverage appears as `meta.source_coverage`; a\ndownstream view may not turn any incomplete observation back into `trusted`.\n\nDiff is partial by design. Its `coverage.classified` list enumerates the coarse\nsource families it can compare. When either of two distinct snapshots says an\nunclassified or reviewed scoped field or unsafe semantic value was omitted, or\neither contains private modifier restriction semantics whose text was\nintentionally omitted, diff emits a `$`\n`unsupported_changes` record, sets `comparison_complete: false`, and reports\nthe relationship as `indeterminate`; an exact identical snapshot can still be\n`unchanged`. Distinct snapshots with the same mechanical revision but a named\npersona/non-mechanical delta are `mechanically_unchanged`, not `unchanged`. A\nchanged mechanical revision that reaches no classifier also falls back to `$`.\nDiff reports candidates or uncertainty only and never applies a change.\nFor controlled D&D Beyond UI research, use the privacy and reversal gates in\n[the A → B → A human differential protocol](docs/human-differential-testing.md).\n\n## Trust and canonical field semantics\n\nThe `trust` block routes every known stat family into one exclusive\nlane:\n\n- `trusted`: no detected finding reaches the field within this version's\n  documented coverage. This is not a global safety or rules-validity claim.\n- `ask_player`: a known ambiguity requires confirmation; `asks` carries the\n  question.\n- `unsupported`: observed content has no applicable handler for that family.\n- `unknown`: observed content has unknown target scope, so derived families\n  fail closed.\n- `invalid`: a known handler received malformed or contradictory source data.\n\nEach material value also has a canonical assessment in `fields` with `value`,\n`state`, `formula`, `inputs`, `sources`, `rules_profile`, `findings`,\n`confidence`, `authority`, `as_of`, `stale`, and `sensitivity`. Field states\nare the closed set `trusted`, `confirm`, `unsupported`, `unknown`, `invalid`,\nand `not_applicable`. Family `ask_player` maps to field `confirm`.\n`not_applicable` is used when a field does not exist for the character, such as\nspellcasting fields for a noncaster.\n\nFail-closed precedence is `invalid` > `unknown` > `unsupported` > `confirm` >\n`trusted`; a projection may preserve or worsen a state, never improve it.\nMutable HP, expended slots, resources, and equipment remain player/session-host\nauthority. `meta.aggregate_state` is the worst material field state and\n`meta.autonomous_ready` is currently always false. Values from one observation\nshare the same default-mechanical `source_revision` and `as_of`.\n\nViews must preserve or worsen trust; they may not remove a material finding.\nConsumers should read `meta`, `fields`, `trust`, `lint`, and `unhandled` rather\nthan treating an aggregate state as proof of completeness. See\n[SUPPORT.md](https://github.com/chaoz23/charactercheck/blob/main/SUPPORT.md).\n\n### D&D Beyond mechanics represented in report schema v2\n\n- Builder-choice rows are joined to modifiers by stable mechanical IDs. This\n  recovers selected skills, languages, and standard tools even when D&D Beyond\n  leaves the modifier's `isGranted` flag false. Builder labels and unselected\n  option catalogs are not retained.\n- Reviewed direct facts now include species walking speed and darkvision,\n  magical-sleep immunity, the closed Charmed-save condition, and numeric\n  spell-group healing bonuses. They retain handler/source provenance and stay\n  non-trusted whenever separate omitted source mechanics reach their family.\n- Armor and shield AC requires `equipped: true`. `combat.weapons` remains an\n  inventory view; `combat.active_attacks` is the action-facing view and\n  includes the 2024 Unarmed Strike. A weapon mastery property is reported\n  separately from `masteries_known` and is never treated as proof that the\n  character learned it.\n- Ordinary `slots_max` comes from the pinned SRD progression and\n  `slots_current` subtracts D&D Beyond's `used` counters. Source-aware\n  `spell_profiles` preserve availability and cast modes when the source\n  exposes them.\n- Public anonymous character payloads can omit class/subclass\n  always-prepared spell collections even when the signed-in sheet displays\n  them. CharacterCheck accepts enriched `alwaysPreparedSpells`,\n  `alwaysKnownSpells`, and `cantrips` collections, but does not invent an\n  absent domain/species/feat spell grant from feature prose. That lane remains\n  unsupported until direct source evidence or a pinned edition-aware resolver\n  is available.\n- Death-save counters expose `active` versus `latent`; source\n  `isStabilized` and rules-implied three-success stability remain distinct.\n  Exhaustion is read from D&D Beyond condition id 4, not from any condition\n  that happens to carry a level.\n\n## Privacy and untrusted text\n\nDefault CLI and MCP mechanical outputs omit D&D Beyond usernames, account\nidentifiers, linked images, appearance, notes, backstory, organizations, and\nverbatim trait/ideal/bond/flaw text. Custom mechanical names may still be\npresent and should be treated as user-authored content.\n\nThe trusted local CLI/library permits explicit persona opt-in for `seatpack`,\n`intake`, or `snapshot` (`--include-persona` in the CLI). Returned persona text\nis bounded, labeled `sensitivity: persona` and\n`content_trust: untrusted_source_text`, and must never be interpreted as\ninstructions. The unauthenticated MCP server exposes neither persona opt-in nor\nhost-local file access. Its former `for_dm` argument was not authorization and\nis not a role boundary. In trusted local `seatpack`/`intake` calls, `--for-dm`\nreplaces all duplicated player-authority live values—current HP/slots, stance,\nresources, and inventory—with an explicit marker; it does not grant DM access.\n\nCharacterCheck has no hosted service, account database, telemetry, cache, or\nserver-side persistence in this repository. The caller controls exported\nfiles, logs, model transfer, and deletion. See\n[PRIVACY.md](https://github.com/chaoz23/charactercheck/blob/main/PRIVACY.md) and\n[SECURITY.md](https://github.com/chaoz23/charactercheck/blob/main/SECURITY.md).\n\n## Exit and error contract\n\nExit status is command-specific:\n\n| Command | `0` | `1` | `2` | `3` |\n|---|---|---|---|---|\n| `derive`, `report` | no lint or unhandled record | lint, with no unhandled record | one or more unhandled records, including unknown/invalid/unsupported states | structured input, retrieval, validation, or internal failure |\n| `diff` | complete comparison with no detected change | any named change or an indeterminate omitted-source/restriction comparison | — | structured input, snapshot, retrieval, or internal failure |\n| `stance`, `qa`, `snapshot`, `quiz`, `seatpack`, `intake` | projection emitted; inspect embedded fields/findings | — | — | structured input, retrieval, validation, or internal failure |\n| `selftest` | smoke test passed | smoke test failed | — | — |\n| `doctor` | all checks passed | — | — | at least one diagnostic check failed |\n\nWith explicit `--table-evaluation`, the shared contract's exit codes apply:\n`0` checked clean, `1` complete with advisories/findings, and `2` incomplete,\nunsupported, invalid, or internal error. Native command behavior is unchanged\nwhen the flag is absent. `--brief` and `--table-evaluation` are mutually\nexclusive.\n\nThe CLI rejects every unsupported command/flag combination with structured\n`bad_flag` exit 2; notably `--for-dm` is valid only for `seatpack` and `intake`.\nMissing arguments,\nunknown commands/options, and `diff` without `--baseline` use argparse's\nplain-text usage error and exit 2. An exit 0 from a projection does not imply\nthat every canonical field is trusted or that autonomous use is safe.\n\nRecognized runtime/input failures return structured `error`, `message`,\n`action`, `retryable`, and `exit_code` fields without serializing the ref, local\npath, source value, or raw exception detail. Unexpected process-boundary\nfailures return `internal_error` plus a correlation ID and log only that ID and\nthe exception class. Library callers receive typed `CharacterCheckError`\ninstances.\n\nThe package-level library surface is deliberately canonical: `derive(ref)` and\n`stance(ref)` retain trust/coverage context. `fetch(ref)` returns a plain\nprivacy-filtered character only when no omission-coverage signal would be lost;\notherwise it raises typed `source_coverage`. The raw arithmetic workspace\nbuilder is internal and is not exported from `charactercheck`. Do not compose a\nplain fetched dict into a new derivation when the source reports schema drift;\nuse `derive`, `snapshot`, or another canonical ref-based view.\n\n## MCP\n\nRun `python3 -m charactercheck.mcp` from the checkout (or\n`charactercheck-mcp` after installing that checkout) over stdio. It exposes\n`derive`, `stance`, `qa`, `diff`, `snapshot`, `seatpack`, `quiz`, `report`,\n`intake`, `selftest`, and `doctor`. It is read-only, accepts public D&D Beyond\nreferences rather than server-local paths, omits persona, and treats mutable\nplayer state conservatively. It does not authenticate a human principal or\nenforce table roles; a trusted host must provide those capabilities before\nactive play.\n\nThe stdio server implements MCP protocol `2025-11-25`, accepts at most an\n8-MiB request line, and emits at most a 4-MiB JSON-RPC response. Tool-domain\nfailures return `isError: true` with the structured error contract. Successful\ntool data is in `structuredContent`; the text block is deliberately a short\ngeneric notice instead of a duplicate of the character object, reducing\nprivacy exposure and context use for clients that support structured results.\n\n## Development and CI\n\n```console\npython3 -m unittest discover -s tests -v\n```\n\nCI's product tests and installed-wheel runtime smokes use only local synthetic\nfixtures or mocked network boundaries, with HTTP(S) routed to a closed local\nport so an accidental character-service request fails. GitHub Actions\ncheckout/setup and installation of the packaging frontend still require their\nnormal GitHub/PyPI bootstrap access, so the workflow as a whole is not an\nair-gapped build. No live player character is a permanent test or advertised\nexample. See\n[CHANGELOG.md](https://github.com/chaoz23/charactercheck/blob/main/CHANGELOG.md)\nand\n[MIGRATING.md](https://github.com/chaoz23/charactercheck/blob/main/MIGRATING.md)\nfor compatibility notes.\n\n## Credits and marks\n\nSchema semantics for the D&D Beyond character-service payload were partly\ninformed by reading [MrPrimate/ddb-importer](https://github.com/MrPrimate/ddb-importer)\n(MIT). The initial adapter registry was independently observed and cross-checked\nagainst its fallback registry; narrow mapping facts and the snapshot/fallback\ndesign were adapted with attribution. See\n[NOTICE](https://github.com/chaoz23/charactercheck/blob/main/NOTICE) and\n[THIRD_PARTY_NOTICES.md](https://github.com/chaoz23/charactercheck/blob/main/THIRD_PARTY_NOTICES.md).\n\nD&D Beyond and Dungeons & Dragons are trademarks of Wizards of the Coast.\nCharacterCheck is an unofficial, independent project and is not endorsed by or\naffiliated with Wizards of the Coast.\n\n<!-- MCP registry ownership marker (do not remove). -->\nmcp-name: io.github.chaoz23/charactercheck\n",
  "bytes": 22924,
  "sha": "4668c7c8026a072cbb555a03da7aec3fe5743b06049466693b9f5fe0ecad2258",
  "repo_slug": "chaoz23/charactercheck",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_chaoz23_charactercheck_6a280bda/readme"
}