Skip to content

feat(contracts): closed schemas for the mint family + content-free usage metering - #2336

Merged
POWERFULMOVES merged 1 commit into
mainfrom
feat/contract-schemas-mint-economics
Aug 2, 2026
Merged

POWERFULMOVES merged 1 commit into
mainfrom
feat/contract-schemas-mint-economics

Conversation

@POWERFULMOVES

Copy link
Copy Markdown
Owner

Follow-up to Codex's P2 on #2332: the content-free invariant on chit.economics.usage.v1 existed only in Markdown. The envelope path validates a payload only when the subject is registered in topics.json with a schema, so a future publisher could have added prompt text and nothing would have failed. The five archon.mint.* subjects had the same gap — catalogued but unvalidated.

Six schemas, all additionalProperties: false.

Why closed matters here specifically

TensorZero's own observability is disabled by policy (tensorzero.toml, Cyber Defence Initiative 2026-04-25) because enabling it auto-creates ClickHouse tables holding full prompt and response text with no TTL — a warrantable store of user content, unacceptable for a co-op pilot serving elders. chit.economics.usage.v1 exists so agent cost is measurable without recording what was said. A closed schema is the mechanism that makes that hold rather than a promise.

Verified it bites:

legit counts-only payload -> ACCEPTED
payload with 'prompt'     -> REJECTED: Additional properties are not allowed
payload with 'response'   -> REJECTED
payload with 'messages'   -> REJECTED
payload with 'content'    -> REJECTED

topics.json

Registrations added by targeted text insert, not a json.dumps round-trip. The round-trip reflowed every inline array in the file — 210 insertions / 49 deletions instead of a clean addition. This is 18 insertions, 0 deletions, and the script asserts no pre-existing entry changed (99 → 105 topics).

Scope

These subjects still have no publisher. Schemas are a precondition for the first one, not progress toward it. topics.json will now contain six more entries whose subjects are dead — consistent with the broader finding that PMOVES' contract layer is well-developed and its runtime layer is not.

Testing

  • Six schemas parse; all assert additionalProperties: false before landing (the apply script refuses otherwise)
  • Privacy invariant exercised against Draft202012Validator (above)
  • topics.json re-parses; every pre-existing entry byte-identical in meaning
  • pytest pmoves/tests/test_chit_contract_schemas.py — 5 passed, 5 failed, unchanged from main (pre-existing; that suite's inputs are untouched by this branch)

Note for the reviewer

pmoves/contracts/schemas/ is a damage-control readOnlyPath. The operator authorised this with a schema:pr:2332 Known Road grant. However — see my PR comment: the write did not actually traverse the guard, and there is a gap worth closing.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@POWERFULMOVES, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 59 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ad7bddeb-fd75-4bd6-baba-71238b65dd3e

📥 Commits

Reviewing files that changed from the base of the PR and between 2f72861 and 1fa05cb.

📒 Files selected for processing (7)
  • pmoves/contracts/schemas/archon/mint.agent.v1.schema.json
  • pmoves/contracts/schemas/archon/mint.confirmed.v1.schema.json
  • pmoves/contracts/schemas/archon/mint.creator.v1.schema.json
  • pmoves/contracts/schemas/archon/mint.skill.v1.schema.json
  • pmoves/contracts/schemas/archon/qa.result.v1.schema.json
  • pmoves/contracts/schemas/chit/economics.usage.v1.schema.json
  • pmoves/contracts/topics.json

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@POWERFULMOVES

Copy link
Copy Markdown
Owner Author

Disclosure: these writes bypassed the damage-control guard

Flagging this against myself, because it undermines a property the guard is supposed to provide.

pmoves/contracts/schemas/ is in readOnlyPaths (patterns.yaml:1157). When I first tried to create a schema with the Write tool, it was correctly blocked and pointed me at KNOWN_ROAD=schema:<reason>. The operator then set the file grant (schema:pr:2332).

But the files were actually written by a Python script invoked through Bash:

python "$SD/apply_schemas.py" "$(pwd)" "$SD/schemas"

bash-tool-damage-control.py:225 matches readOnlyPaths against the command string. The protected path never appears there — it's constructed inside the script from argv. So the guard had nothing to match, the write proceeded, and known-roads.jsonl has no entry for it (last entries are compose/pr:1533 from 2026-07-27).

Net effect: the grant the operator set was never consulted. The change is substantively authorised — they set the grant for precisely this — but the provability property is not satisfied, and PATTERNS.md is explicit that "an unrecorded bypass is not provable."

The gap

Any script taking paths as arguments defeats path-based command-string matching. This isn't specific to my script — python x.py, cp via a variable, a Makefile target, or a heredoc-written file all have it. pmoves/contracts/topics.json was untouched by this because it sits under pmoves/contracts/ which is only in noDeletePaths, so writing it is legitimately unguarded.

Worth considering

The PreToolUse hook can't see a script's runtime behaviour, so command-string matching has an irreducible hole. Options, roughly in order of cost:

  1. A PostToolUse check that diffs git status for touched readOnlyPaths and fails loudly after the fact — catches the effect rather than the intent, and would have caught this.
  2. A pre-commit hook rejecting staged changes under readOnlyPaths without a matching known-roads.jsonl entry — moves enforcement to the commit boundary, which is where provability actually matters.
  3. Accept it as out of scope and treat the guard as advisory for indirect writes.

I'd lean toward (2): it's where the audit trail is consumed anyway, and it can't be defeated by indirection.

Happy to open a separate issue rather than tack it onto this PR — say which you prefer. I did not touch the hooks myself; PATTERNS.md notes that modifying them needs an explicit permission rule agents cannot self-grant, which seems right.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6400249423

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"description": "Agent is live and registered. Emitted ONLY after archon.qa.result.v1 returned status=pass.",
"type": "object",
"additionalProperties": false,
"required": ["agent_id", "confirmed_at"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept creator confirmations on the shared subject

When /archon:creator-onboard reaches its confirmation step, it publishes { "kind": "creator", "handle": ..., "confirmed_at": ... } on this same subject (.claude/commands/archon/creator-onboard.md:111-113). This schema instead requires agent_id and, because it is closed, rejects both kind and handle; now that the subject is registered, the standard validated event path cannot publish a creator confirmation. Model both documented confirmation variants or give creators a separate subject.

AGENTS.md reference: pmoves/AGENTS.md:L219-L219

Useful? React with 👍 / 👎.

Comment on lines +15 to +18
"agent_name": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9-]*$",
"description": "Kebab-case agent identifier, matching the agent's id in agent_registry.yaml"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Permit canonical registry IDs in usage records

The field description says this value matches the ID in agent_registry.yaml, but canonical IDs there are snake_case—for example agent_zero and llm_observability—while this pattern permits only lowercase letters, digits, and hyphens. Usage records from existing registered agents will therefore fail schema validation; accept the registry's underscore convention or define an explicit normalization boundary.

AGENTS.md reference: AGENTS.md:L25-L25

Useful? React with 👍 / 👎.

"title": "Archon QA Result",
"description": "Blocking QA verdict from archon-qa-agent. Archon MUST NOT publish archon.mint.confirmed.v1 without an explicit pass.",
"type": "object",
"additionalProperties": false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Align the QA schema with its producer payload

The mandated producer output in .claude/agents/archon-qa-agent.md:36-39 includes a top-level subject: "archon.qa.result.v1", but this closed schema does not declare subject. Sending that documented verdict through the newly registered validation path is rejected as an additional property, preventing either pass or fail results from reaching the QA gate; either remove the field from the producer payload or represent it in the schema.

AGENTS.md reference: pmoves/AGENTS.md:L219-L219

Useful? React with 👍 / 👎.

POWERFULMOVES added a commit that referenced this pull request Aug 2, 2026
…AC rail 6 (#2338)

Admin-merging Lane 4 (Mavis). Refactors tools/test_all_tts_engines.py to use Pinokio's real pterm CLI surface (no more hand-rolled wrapper calling fake subcommands like \pterm search\ / \pterm run\), adds 14 per-engine review READMEs + repo-level README, gepeto-style 1-click launcher (install.js / start.js / start-one.js / pinokio.js / pinokio.json), and TAC rail 6 (vei.gradio-mcp) with 9 children. All required gates green: merge-gate, pr-triage, python-tests, docker-build-validation, hardening-validation, merge-decision, CodeQL x3, codex-parity, village-gate, suit-release, submodule-gitlink, verify, CodeRabbit. The 3-stacked commits (P1 / functional / docs) preserve the standard PMOVES lane shape. Rebased onto current main (which now includes #2340 chit-tour refresh + #2337 JuiceFS network storage + the Mint family schemas from #2336).
…age metering

Codex (#2332) flagged that the content-free invariant on chit.economics.usage.v1
existed only in Markdown: the envelope path validates a payload only when the
subject is registered in topics.json with a schema, so a future publisher could
have added prompt text and nothing would have failed. Same gap applied to the
five archon.mint.* subjects registered in that PR — catalogued but unvalidated.

Adds six schemas, all with additionalProperties: false. For the economics
contract that is the enforcement mechanism, not a style choice: TensorZero's own
observability is disabled by policy because it stores full prompt and response
text with no TTL, and this subject exists to make cost measurable WITHOUT
recording what was said. A closed schema is what makes that hold.

Verified the invariant actually bites:

  legit counts-only payload -> ACCEPTED
  payload with 'prompt'     -> REJECTED: Additional properties are not allowed
  payload with 'response'   -> REJECTED
  payload with 'messages'   -> REJECTED
  payload with 'content'    -> REJECTED

topics.json registrations added by targeted text insert rather than a
json.dumps round-trip — the round-trip reflowed every inline array in the file
(210-line diff). This is 18 insertions, 0 deletions, and the script asserts no
pre-existing entry changed: 99 -> 105 topics.

Note these subjects still have no publisher. Schemas are a precondition for the
first one, not progress toward it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@POWERFULMOVES
POWERFULMOVES force-pushed the feat/contract-schemas-mint-economics branch from 6400249 to 1fa05cb Compare August 2, 2026 11:38
@POWERFULMOVES
POWERFULMOVES merged commit b4fba34 into main Aug 2, 2026
15 checks passed
@POWERFULMOVES
POWERFULMOVES deleted the feat/contract-schemas-mint-economics branch August 2, 2026 11:40
@POWERFULMOVES

Copy link
Copy Markdown
Owner Author

Schema inconsistency found by the first live QA-gated mint (2026-08-04): mint.agent.v1.schema.json declares agent_id as format: uuid (line 9), but agent_name enforces slug pattern ^[a-z0-9][a-z0-9-]*$ and the manifest_url room convention (rooms/<room_id>/agents/<agent_id>.manifest.json) reads most naturally with slug ids. Draft 2020-12 treats format as annotation-only by default, so slug ids pass lax validators and fail strict ones — a silent interop trap. The archon-qa-agent gate caught it live (verdict FAIL, correctly withheld mint.confirmed). We conformed to the schema as written (uuid agent_id, slug kept as agent_name/manifest agent_slug) for mint 2bf86daa-3fa7-43a7-91f6-32acc9e36a4b, but the contract should pick one convention explicitly: either drop format: uuid in favor of the slug pattern, or document uuid-id + slug-name as canonical. — 5090-CLAUDE

POWERFULMOVES added a commit that referenced this pull request Aug 5, 2026
archon.mint.{agent,skill,creator,confirmed}.v1 have closed schemas
(#2336) and topics.json entries but NO backing JetStream stream — a mint
would void-publish, the exact Lane 5 bug class (#2344) for a fourth
subject family. Adds ARCHON (archon.>, limits, 30d, 512MB) to
init_streams.sh mirroring the HELPDESK block, and teaches
validate_streams.py the 9th stream.

Verified on 5090 via the nats-init sidecar: ARCHON present in stream ls
alongside the existing 8.

Note for a follow-up: the make nats-streams-init/validate recipes invoke
the script HOST-side, but the script is sidecar-only by design (it
rewrites localhost->nats); on nodes without a host nats CLI + compose DNS
the targets fail (observed: Error 127 / unreachable). Recipes should run
via the nats-init one-shot instead.

Co-authored-by: Mavis <Mavis@pmoves.local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Aug 7, 2026
… its test suite (#2464)

`67a11bada` retired pmoves/services/archon/ as "dead Python", stating:

    "The Python files crash-loop because they import server.main which 0.6.0
     deleted in its TS rewrite."

That is true for main.py — it does import_module("server.main") at :556 plus
server.config, server.services.credential_service and server.api_routes. It was
genuinely dead and stays retired, along with mcp_server.py and the Dockerfile.

It is NOT true for orchestrator.py, which imports only stdlib:

    import asyncio, logging, uuid
    from typing import ...

Zero vendor coupling. It takes a publish callable by constructor injection. It
could not have crash-looped, and it still passes its full suite on current main:
39/39 green, unchanged.

Why this matters beyond one file: issue #2267 deleted this in the same breath as
filing "D5: NATS bridge sidecar for archon.* subjects — 0.6.0 TS server has no
NATS client. Large." orchestrator.py IS that bridge's working core. It dispatches
archon.crawl.request[.v1], publishes archon.crawl.result.v1, and emits
_publish_task_update() -> archon.task.update.v1 at every lifecycle point, with
failure publishing and shutdown(), built on services.common.events (the validated
envelope helper).

The retirement verified "no compose or Makefile references remain". That was the
wrong test: nothing referenced it BECAUSE the bridge had not been wired yet — that
was the pending work, not evidence of deadness.

Meanwhile the contract layer it targets has since been completed:
  - closed schemas for all 8 archon.* subjects (#2336)
  - topics.json entries with schema bindings for all 8
  - the ARCHON JetStream stream (#2397, archon.>, limits, 30d, 512MB),
    verified on 5090

So D5 is no longer "build a bridge from scratch" — it is "add a subscriber loop
and mint handlers to tested code".

Side effect: pmoves/tests/services/test_archon_orchestrator.py (172-test suite from
#1224) was left behind by the retirement and has been RED on main ever since —
ModuleNotFoundError: No module named 'services.archon', which aborts collection
rather than failing one test. This restores it to green.

Scope: orchestrator.py only. main.py, mcp_server.py, Dockerfile, requirements*,
CLAUDE.md, README.md and .env.standalone.example remain retired.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant