feat: add schema and record entry types with JSON Schema validation#287
Open
cmeans-claude-dev[bot] wants to merge 24 commits intomainfrom
Open
feat: add schema and record entry types with JSON Schema validation#287cmeans-claude-dev[bot] wants to merge 24 commits intomainfrom
cmeans-claude-dev[bot] wants to merge 24 commits intomainfrom
Conversation
Full design for implementing EntryType.SCHEMA and EntryType.RECORD — JSON Schema Draft 2020-12 validation on write, per-owner with _system fallback, absolute schema immutability, record re-validation on update, CLI-only writes to _system owner, structured validation error envelope. Closes design phase for #208. Implementation plan follows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bite-sized TDD plan covering 19 tasks: new validation module, two Store protocol methods, two MCP tools, update_entry/delete_entry branching, Alembic _system user seed, CLI tool, docs, pre-push verification, and PR. Full self-review cross-checks every D1-D8 decision and error code against a concrete task. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds find_schema(owner_id, logical_key) to the Store protocol and PostgresStore. A single SQL query with CASE-based ORDER BY returns the caller's own schema when present, falling back to the _system-owned version. Soft-deleted entries are excluded. Seeds the _system user in the test fixture and adds 5 tests covering all lookup scenarios. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds count_records_referencing to the Store protocol and PostgresStore. Returns (total_count, first_10_ids) of non-deleted records that reference a given schema logical key (decomposed via rpartition on the last ':'). Backed by two SQL files following the one-operation-per-file convention. Five tests cover zero, match, soft-delete exclusion, version isolation, and the 10-id cap with 15 records. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Idempotent INSERT ON CONFLICT DO NOTHING seeds the _system user row so entries with owner_id='_system' have a valid owner. Includes idempotence test verifying the ON CONFLICT path does not create duplicates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the register_schema tool with JSON Schema Draft 2020-12 validation, duplicate detection via psycopg UniqueViolation, and integration tests. Also registers the tool in server.py re-exports and updates TestWriteResponseShapes to cover the new tool. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…allback Resolves schema by ref+version (caller-owned first, _system fallback), validates content via validate_record_content, and upserts on logical_key. Raises structured ToolError with validation_errors list on schema mismatch. Truncation sentinel from validate_record_content is promoted to top-level envelope fields. TestWriteResponseShapes updated for create_record. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds **extras: Any kwargs to _error_response so structured fields beyond the fixed set (schema_ref, schema_version, searched_owners, validation_errors, etc.) can flow through the error envelope uniformly without raw ToolError construction at call sites. Adds TestErrorResponseExtras unit tests verifying extras appear in the raised ToolError JSON payload and do not clobber the fixed fields. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds type-specific branching in update_entry after the updates dict is built but before the store write: - SCHEMA entries: always rejected with schema_immutable error - RECORD entries + content kwarg: re-validates content against the registered schema (resolver uses _system fallback); rejects with validation_failed including structured per-error list - RECORD entries + no content kwarg: passes through unchanged (description-only updates skip re-validation) Also refactors create_record to use _error_response with **extras instead of raw ToolError(json.dumps(...)) construction, removing the last two raw-ToolError sites from the tools module. Adds 4 integration tests covering all branches. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Soft-deleting a schema entry is now blocked when live (non-deleted) records still reference it. The check runs in the by-id path only; bulk deletes by tags/source do not include schemas in scope. Adds three integration tests covering: no-records-succeeds, with-records-rejected, and allowed-after-records-deleted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Operator bootstrap tool for seeding _system-owned schema entries directly via PostgresStore — no MCP auth or middleware involved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…/record Append three new integration tests to verify: 1. test_cross_owner_schema_invisible — Owner A registers a schema; Owner B cannot resolve it. Verifies owner isolation at the tool boundary. 2. test_both_owners_see_system_schema — Both owners can use a _system schema. Verifies _system fallback works cross-owner. 3. test_caller_schema_overrides_system — When both _system and caller have the same logical_key, caller's version wins. Verifies override semantics via schema body difference (integer vs string). All three tests use the configured_server fixture with monkeypatch for owner switching. Tests verify the design point from Task 6: resolve_schema → find_schema query prefers caller-owned over _system via SQL CASE ORDER BY. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add schema/record documentation to CHANGELOG, README (tool count 30→32, new tool descriptions), data-dictionary (new entry type specs), and server instructions. Include jsonschema dependency note and _system namespace details. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add mypy override for jsonschema.* (no stubs available), remove now-unnecessary type: ignore comments that mypy flagged as unused. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-error paths Also fixes two latent bugs in tools.py: - register_schema: except Exception fallback was unreachable (lines 621-630) — now exercised via generic unique-violation mock - create_record: jse.JsonSchemaException doesn't exist in jsonschema 4.x — replaced with except Exception; removed now-unused jse import Brings tools.py to 100%, cli_register_schema.py to 98%, validation.py to 100%. Total: 945 tests collected (938 pass, 7 skip). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tests used @pytest.mark.asyncio (local env had pytest-asyncio) but the repo's established pattern is @pytest.mark.anyio via anyio plugin. Also fixed ruff I001/E501 from Task 18 coverage tests and removed a now-unused # type: ignore in cli_register_schema.py. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds two new
EntryTypevalues —schemaandrecord— with JSON Schema Draft 2020-12 validation on write. Schemas live per-owner with a shared_systemfallback namespace for built-in shapes; records pin to an exact(schema_ref, schema_version)and get re-validated on content update. Schema deletion is blocked while live records still reference the version. A new CLI tool (mcp-awareness-register-schema) gives operators a path to seed_system-owned schemas at deploy time. The internal_error_response()helper now accepts**extras, so all new structured error envelopes flow through the same path.Closes #208.
Spec:
docs/superpowers/specs/2026-04-13-schema-record-entry-types-design.mdPlan:
docs/superpowers/plans/2026-04-13-schema-record-entry-types-plan.mdWhat's new
register_schema,create_recordmcp-awareness-register-schema --system ...— operator-only, bypasses MCP_systemuser seedjsonschema>=4.26.0,<5validation.validate_schema_body,validation.validate_record_content,validation.resolve_schema,validation.assert_schema_deletable,validation.compose_schema_logical_key,SchemaInUseErrorfind_schema(owner_id, logical_key)(with_systemfallback),count_records_referencing(owner_id, schema_logical_key)(returns(count, up_to_10_ids))update_entrybranches on type — schemas always reject (schema_immutable), records re-validate content on update;delete_entryblocks schema deletion when records reference it (schema_in_use);_error_response()now accepts**extrasfor structured error payloadsWhat's explicitly out of scope
is_admincolumn onusers; system writes are CLI-only for now.$refresolution viareferencing.Registry— deferred until a real use case demands it.delete_entryby tags/source) — not protected against live records. Single-id deletion is protected. Bulk protection is a known concern worth its own follow-up.create_entry— kept type-specific tools per existing convention; a future refactor across all write tools could collapse them, but that's not this PR.Deployment
After merge + Docker image rebuild:
mcp-awareness-migratein each environment to apply the_systemuser seed.mcp-awareness-register-schema --system ...per built-in schema, gradually as schemas are authored.QA
Prerequisites
pip install -e ".[dev]"AWARENESS_PORT=8421) viadocker-compose.qa.yaml.mcp-awareness-migrateagainst the QA DB to apply the_systemuser seed migration.Manual tests (via MCP tools unless otherwise noted)
Expected:
{"status":"ok","id":"<uuid>","logical_key":"schema:qa-thing:1.0.0"}Expected: structured error with
code: "invalid_schema"in the payload.Re-run step 1 exactly. Expected:
code: "schema_already_exists"withlogical_keyin the extras.Expected:
code: "invalid_parameter"withparam: "family".Expected:
{"status":"ok","id":"<uuid>","action":"created"}Expected:
code: "validation_failed"withvalidation_errorslist containing at least therequiredviolation for the missingnamefield, andschema_ref/schema_versionechoed.Expected:
code: "schema_not_found",searched_owners: [<your-owner>, "_system"].Re-run step 5 with different content
{"name": "widget-v2"}. Expected:action: "updated", sameidas step 5.Expected: success (no error). Content updated.
Expected:
code: "validation_failed"; record content unchanged when queried viaget_knowledge.Expected:
code: "schema_immutable"; schema unchanged.Expected:
code: "schema_in_use",referencing_records: [...],total_count.delete_entry(entry_id=<record id from step 5>), then retry step 12.Expected: schema soft-deletes successfully.
_systemfallback via CLI + MCPOn the QA instance shell:
Then via MCP:
Expected: record creates successfully against the
_system-owned schema.As a second authenticated user on the QA instance, attempt to resolve step 1's schema (
schema:qa-thing:1.0.0). Expected:code: "schema_not_found".Edge cases
Expected: success —
data.contentcan be any JSON value, not just objects.Register
{"type": "array", "items": {"type": "string"}}and write["a","b"]as content. Expected: success.Known limitations (verify no attempt exploits these)
delete_entryby tags/source) do not currently protect schemas referenced by records. Only single-id deletion is protected. If QA has a compelling case for bulk-delete protection, file as follow-up; not blocking for this PR.