fix(schema): attest authoritative inference receipts - #3830
Conversation
Problem: schema generation could run without proving that the archive was quiesced and that every schema tier matched the packaged schema. What changed: add an archive-wide offline lease, content-bound immutable PASS receipts, live archive and tier identity validation, and a required receipt to the schema-generate command. Add command-route attack tests for missing, mismatched, replaced, stale, stale-tier, and concurrent-writer cases. Compatibility/migration: schema-generate now requires --schema-inference-receipt. Existing aggregate generation receipts remain separate and are not authoritative gate receipts. Co-Authored-By: Claude <noreply@anthropic.com>
Schema package persistence now consumes a fresh, HMAC-attested gate receipt. The gate rejects empty evidence and archive-owned blob paths from being used as external ground truth, while both persisting CLI paths hold the archive exclusion lease through generation. Verification: devtools test focused schema gate and command suites; devtools verify --quick.
|
Warning Review limit reached
Next review available in: 27 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (10)
📝 WalkthroughWalkthroughSchema inference now uses immutable, attested receipts. The gate validates archive and tier identities, schema compatibility, freshness, paths, and quiescence before authorizing schema generation. Persistent CLI operations require a receipt. ChangesSchema authorization flow
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant SchemaGate
participant Archive
participant Receipt
participant SchemaCLI
participant SchemaGenerator
SchemaGate->>Archive: acquire exclusive quiescence
SchemaGate->>Receipt: write immutable attested receipt
SchemaCLI->>Receipt: validate receipt and archive identities
Receipt-->>SchemaCLI: return authorization result
SchemaCLI->>SchemaGenerator: generate schemas within authorization
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@devtools/schema_generate.py`:
- Around line 119-120: Introduce a shared
resolve_authorized_archive_root(config) helper beside
authorize_schema_generation that returns the configured archive root while
preserving _ConfigStub compatibility. Replace the duplicated inline derivation
in devtools/schema_generate.py lines 119-120, devtools/schema_commit.py lines
106-107, and polylogue/schemas/operator/schema_inference.py line 142 with this
helper before authorization; retain the separate --db-path containment check in
schema_inference.py.
In `@polylogue/maintenance/schema_inference_gate.py`:
- Around line 530-565: Avoid recomputing tier file hashes during a single
schema-gate operation: update the identity collection flow around
_tier_schema_identity, _run_schema_inference_gate_locked, and
validate_schema_inference_gate_receipt to reuse content digests keyed by path
and file metadata (st_dev, st_ino, st_size, and st_mtime_ns), or collect them
once and pass them into the final snapshot. Preserve the existing identity and
validation results while ensuring each unchanged tier file is hashed only once
per gate run.
- Around line 1257-1262: Update authorize_schema_generation to enforce
single-use authorization rather than only validating the receipt: after
successful validation, atomically consume or claim its receipt_nonce in a
durable store, or require the validated receipt to be removed/renamed so
subsequent calls fail. Preserve schema_inference_quiescence and ensure failed
validation does not consume the authorization.
In `@polylogue/schemas/operator/schema_inference.py`:
- Around line 127-137: Update the parser definition for schema_inference_receipt
to mark --schema-inference-receipt as required, matching the existing argparse
pattern in related commands. Remove the redundant None check and ensure any
remaining command error output in this flow is written to stderr, consistent
with the other devtools commands.
In `@tests/unit/devtools/test_schema_commit_command.py`:
- Around line 28-32: Update the _allow_schema_generation test stub to record
each authorization call and its receipt argument, then assert non-dry-run commit
tests enter the context exactly once with the command-line receipt path. Add an
assertion that the dry-run path does not enter authorization, covering the with
authorize_schema_generation(...) flow in the commit command.
In `@tests/unit/devtools/test_schema_lab_commands.py`:
- Line 32: Move the shared _seed_archive helper out of
tests.unit.maintenance.test_schema_inference_gate into a shared fixture module
under tests/infra, then update both test_schema_lab_commands.py and
test_schema_inference_gate.py to import it from that module. Remove the
cross-package import so devtools test collection does not load the maintenance
test module.
- Around line 383-403: Update
test_schema_generate_rejects_replaced_or_stale_receipt to assert the exact
expected error for each mutation instead of accepting a disjunction of messages.
Preserve the replaced-receipt case for attestation failure, and test staleness
using a genuinely signed receipt passed to
validate_schema_inference_gate_receipt with an advanced now value beyond
RECEIPT_TTL (while respecting RECEIPT_CLOCK_SKEW), so the freshness-window logic
is exercised directly.
- Around line 355-365: Make
test_schema_generate_valid_route_consumes_fresh_gate_receipt deterministic by
requesting the frozen_clock fixture and patching the runtime module’s datetime
source used to evaluate receipt freshness. Set the frozen time consistently with
the authoritative receipt so the test does not depend on wall-clock timing; use
fixed_now only for expected timestamp construction where applicable.
In `@tests/unit/maintenance/test_schema_inference_gate.py`:
- Around line 186-193: Update the test helper _run so its default ground-truth
root mapping is applied only when ground_truth_roots is omitted, not when it is
explicitly an empty dict; use an optional/sentinel distinction while preserving
the existing default mapping for callers that do not supply the argument. This
ensures test_empty_archive_cannot_authorize_schema_inference exercises an
actually empty root set.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 653242b1-da6d-457a-8a33-c89e0b11a85d
📒 Files selected for processing (7)
devtools/schema_commit.pydevtools/schema_generate.pypolylogue/maintenance/schema_inference_gate.pypolylogue/schemas/operator/schema_inference.pytests/unit/devtools/test_schema_commit_command.pytests/unit/devtools/test_schema_lab_commands.pytests/unit/maintenance/test_schema_inference_gate.py
| def _tier_schema_identity(archive_root: Path, location: ArchiveLocation) -> dict[str, object]: | ||
| tiers: dict[str, object] = {} | ||
| for tier, spec in ARCHIVE_TIER_SPECS.items(): | ||
| path = location.active_index_path if tier is ArchiveTier.INDEX else archive_root / spec.filename | ||
| identity = ( | ||
| location.active_index if tier is ArchiveTier.INDEX else location.configured_tier(cast(Any, tier.value)) | ||
| ) | ||
| path = identity.resolved_path | ||
| expected_schema_sha256 = _canonical_schema_digest(tier) | ||
| entry: dict[str, object] = { | ||
| "path": str(path), | ||
| "stable_id": identity.stable_id, | ||
| "expected_user_version": spec.version, | ||
| "durability": spec.durability, | ||
| "exists": path.exists(), | ||
| "actual_user_version": None, | ||
| "content_sha256": None, | ||
| "schema_sha256": None, | ||
| "expected_schema_sha256": expected_schema_sha256, | ||
| } | ||
| if path.exists(): | ||
| try: | ||
| with open_readonly_connection(path) as conn: | ||
| entry["actual_user_version"] = int(conn.execute("PRAGMA user_version").fetchone()[0]) | ||
| entry["schema_sha256"] = _sqlite_schema_digest(conn) | ||
| except sqlite3.Error as exc: | ||
| entry["error"] = str(exc) | ||
| try: | ||
| entry["content_sha256"] = _file_sha256(path) | ||
| except OSError as exc: | ||
| entry["error"] = str(exc) | ||
| entry["matches_expected"] = entry["actual_user_version"] == spec.version | ||
| if expected_schema_sha256 is not None: | ||
| entry["matches_expected"] = bool(entry["matches_expected"]) and ( | ||
| entry["schema_sha256"] == expected_schema_sha256 | ||
| ) | ||
| tiers[tier.value] = entry |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
Full-file hashing of every tier repeats on each identity collection.
_tier_schema_identity computes _file_sha256 for every tier file. _run_schema_inference_gate_locked calls it twice (Line 1282 and Line 1347), and validate_schema_inference_gate_receipt calls it again on each authorized CLI run (Line 1238). On a large archive this reads every tier database three or more times per operation.
Cache the per-path digest by (path, st_dev, st_ino, st_size, st_mtime_ns) inside one gate run, or collect content hashes once and reuse them for the final identity snapshot.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@polylogue/maintenance/schema_inference_gate.py` around lines 530 - 565, Avoid
recomputing tier file hashes during a single schema-gate operation: update the
identity collection flow around _tier_schema_identity,
_run_schema_inference_gate_locked, and validate_schema_inference_gate_receipt to
reuse content digests keyed by path and file metadata (st_dev, st_ino, st_size,
and st_mtime_ns), or collect them once and pass them into the final snapshot.
Preserve the existing identity and validation results while ensuring each
unchanged tier file is hashed only once per gate run.
| @contextmanager | ||
| def authorize_schema_generation(archive_root: Path, receipt_path: Path) -> Iterator[dict[str, object]]: | ||
| """Hold quiescence while authorizing one schema-generation scan.""" | ||
|
|
||
| with schema_inference_quiescence(archive_root): | ||
| yield validate_schema_inference_gate_receipt(receipt_path, archive_root=archive_root) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Look for any consumption/replay tracking for receipt_nonce.
rg -n -C4 'receipt_nonce|consumed|nonce' --glob '*.py' -g '!tests/**'Repository: Sinity/polylogue
Length of output: 154
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## schema_inference_gate.py outline"
ast-grep outline polylogue/maintenance/schema_inference_gate.py --view expanded | sed -n '1,220p'
echo "## relevant function definitions"
sed -n '1180,1395p' polylogue/maintenance/schema_inference_gate.py | cat -n | sed -n '1,230p'
echo "## search receipt_nonce in all tracked Python files"
git ls-files '*.py' | xargs rg -n -C3 'receipt_nonce|consumed|nonce' || trueRepository: Sinity/polylogue
Length of output: 50373
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## exact production receipt_nonce occurrences"
git grep -n -C3 'receipt_nonce' -- '*.py' '!(tests/*)' || true
echo "## exact authorized schema generation contexts"
rg -n -C4 'authorize_schema_generation\(' --glob '*.py' tests || true
echo "## local receipt_nonce validation code block"
ast-grep run --pattern 'def validate_schema_inference_gate_receipt(...\n{
$$$
}' --lang python polylogue/maintenance/schema_inference_gate.py || trueRepository: Sinity/polylogue
Length of output: 2145
Other (CWE-294): Authentication Bypass by Capture-replay
Reachability: Internal
Reachability path
● Entry
tests/unit/devtools/test_schema_commit_command.py
│
▼
● Hop
devtools/schema_commit.py:76
main
│
▼
● Sink
polylogue/maintenance/schema_inference_gate.py
Do not call authorize_schema_generation single-use without consuming the receipt.
authorize_schema_generation says it authorizes one scan, but it only calls validate_schema_inference_gate_receipt. That validator accepts the same PASS receipt on every invocation until RECEIPT_TTL expires. The generated receipt_nonce is not checked or recorded, so it does not prevent replay. Either enforce single use (consume the nonce/claim in a durable store or require callers to remove/rename the receipt after successful authorization) or update the docstring to allow reuse while the receipt is fresh and the archive identity is unchanged.
[semantic_category:broken_auth]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@polylogue/maintenance/schema_inference_gate.py` around lines 1257 - 1262,
Update authorize_schema_generation to enforce single-use authorization rather
than only validating the receipt: after successful validation, atomically
consume or claim its receipt_nonce in a durable store, or require the validated
receipt to be removed/renamed so subsequent calls fail. Preserve
schema_inference_quiescence and ensure failed validation does not consume the
authorization.
| @contextmanager | ||
| def _allow_schema_generation(*_args: object, **_kwargs: object) -> Iterator[dict[str, object]]: | ||
| yield {} | ||
|
|
||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
No test proves that the commit path enters the authorization context.
_allow_schema_generation accepts any arguments and yields. Every non-dry-run test patches it, so removing the with authorize_schema_generation(...) block in devtools/schema_commit.py Line 107 would keep all these tests green. The only new negative test covers the missing --schema-inference-receipt argument, which is checked before authorization.
Record the call in the stub and assert it, and assert that a dry-run does not enter it.
💚 Proposed fix to pin the authorization call
+_authorized: list[tuple[object, ...]] = []
+
+
`@contextmanager`
def _allow_schema_generation(*_args: object, **_kwargs: object) -> Iterator[dict[str, object]]:
+ _authorized.append(_args)
yield {}Then assert len(_authorized) == 1 and that the recorded receipt path matches the argument passed on the command line.
Also applies to: 52-56
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/unit/devtools/test_schema_commit_command.py` around lines 28 - 32,
Update the _allow_schema_generation test stub to record each authorization call
and its receipt argument, then assert non-dry-run commit tests enter the context
exactly once with the command-line receipt path. Add an assertion that the
dry-run path does not enter authorization, covering the with
authorize_schema_generation(...) flow in the commit command.
| def test_schema_generate_valid_route_consumes_fresh_gate_receipt( | ||
| monkeypatch: pytest.MonkeyPatch, | ||
| tmp_path: Path, | ||
| capsys: pytest.CaptureFixture[str], | ||
| ) -> None: | ||
| root, receipt = _authoritative_schema_gate(tmp_path, "valid") | ||
| monkeypatch.setattr(schema_generate, "get_config", lambda: _ConfigStub(root / "index.db", root)) | ||
| monkeypatch.setattr(schema_generate, "infer_schema", _successful_inference) | ||
|
|
||
| assert schema_generate.main(["--provider", "chatgpt", "--schema-inference-receipt", str(receipt)]) == 0 | ||
| assert "Generated schema package set for chatgpt" in capsys.readouterr().out |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Locate the frozen_clock fixture and the uses_real_clock marker convention.
rg -n -C6 'def frozen_clock|uses_real_clock' --glob '*.py'Repository: Sinity/polylogue
Length of output: 154
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== tracked Python files with schema_lab_commands =="
git ls-files | rg '(^tests/.*schema_lab_commands\.py$|schema_generate|inference|receipt|frozen_clock|uses_real_clock)' || true
echo
echo "== target file snippet =="
target="tests/unit/devtools/test_schema_lab_commands.py"
if [ -f "$target" ]; then
wc -l "$target"
sed -n '320,420p' "$target" | cat -n -v
else
echo "missing $target"
fi
echo
echo "== search target tests without glob =="
rg -n -C4 'test_schema_generate_valid_route_consumes_fresh_gate_receipt|test_schema_generate.*stale|validate_schema_inference_gate_receipt|schema_inference_receipt|generated_at|RECEIPT_TTL|time\.time|datetime\.now|utcnow|frozen_clock|uses_real_clock' -g '*.py' || trueRepository: Sinity/polylogue
Length of output: 8327
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== repository root files =="
git show refs/remotes/origin/HEAD:pyproject.toml >/tmp/pyproject-check.txt 2>/dev/null || true
if [ -f pyproject.toml ]; then
sed -n '1,220p' pyproject.toml | cat -n -v
fi
echo
echo "== tests/unit/devtools/test_schema_inference_gate.py =="
wc -l tests/unit/devtools/test_schema_inference_gate.py
sed -n '1,260p' tests/unit/devtools/test_schema_inference_gate.py | cat -n -v
echo
echo "== tests/unit/infra/test_frozen_clock.py =="
wc -l tests/unit/infra/test_frozen_clock.py
sed -n '1,220p' tests/unit/infra/test_frozen_clock.py | cat -n -v
echo
echo "== tests/infra/frozen_clock.py =="
wc -l tests/infra/frozen_clock.py
sed -n '1,220p' tests/infra/frozen_clock.py | cat -n -v
echo
echo "== direct references in schema inference gate and schema generate =="
for f in devtools/schema_inference_gate.py devtools/schema_generate.py tests/infra/frozen_clock.py tests/unit/infra/test_frozen_clock.py; do
echo "-- $f --"
rg -n -C3 'datetime\.now|time\.time|utcnow|utcfromtimestamp|epoch|now\(|tz_utc|UTC|RECEIPT_TTL|generated_at|frozen_clock|uses_real_clock|pytest' "$f" || true
doneRepository: Sinity/polylogue
Length of output: 36148
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== repo test conftest files =="
git ls-files | rg '(^|/)conftest\.py$' || true
echo
echo "== search uses_real_clock across tracked Python files only =="
python3 - <<'PY'
import subprocess, pathlib
files = subprocess.check_output(["git", "ls-files", "*.py"], text=True).splitlines()
matches = []
for f in files:
txt = pathlib.Path(f).read_text(errors="ignore")
if "uses_real_clock" in txt or "frozen_clock" in txt:
matches.append((f, [(i, txt.splitlines()[i-1]) for i, line in enumerate(txt.splitlines(), start=1) if "uses_real_clock" in line or "frozen_clock" in line]))
for f, hits in matches:
print(f"-- {f} --")
for line, text in hits[:40]:
print(f"{line}: {text}")
if len(hits) > 40:
print(f"... +{len(hits)-40}")
PY
echo
echo "== target test file includes and fixture signature context =="
sed -n '1,50p' tests/unit/devtools/test_schema_lab_commands.py | cat -n -v
sed -n '336,370p' tests/unit/devtools/test_schema_lab_commands.py | cat -n -v
sed -n '388,410p' tests/unit/devtools/test_schema_lab_commands.py | cat -n -vRepository: Sinity/polylogue
Length of output: 29297
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== tests/conftest.py =="
sed -n '1,130p' tests/conftest.py | cat -n -v
echo
echo "== tests/unit/infra/test_clock_guard.py =="
sed -n '1,90p' tests/unit/infra/test_clock_guard.py | cat -n -v
echo
echo "== pytest runtime source for frozen_clock guard =="
python3 - <<'PY'
import pathlib
fp = pathlib.Path("tests/unit/infra/test_clock_guard.py")
text = fp.read_text(errors="ignore")
lines = text.splitlines()
for i, line in enumerate(lines, 1):
if "frozen_clock" in line or "uses_real_clock" in line or "check_time" in line:
start=max(1,i-8); end=min(len(lines),i+12)
print(f"\n--- around {i} ---")
for n in range(start,end+1):
print(f"{n}: {lines[n-1]}")
PYRepository: Sinity/polylogue
Length of output: 21822
Use frozen_clock for timestamp-sensitive receipt tests.
test_schema_generate_valid_route_consumes_fresh_gate_receipt passes only because the receipt is evaluated immediately, and the stale branch at lines 395–396 relies on real wall-clock time being past the fixed 2020 timestamp. Keep datetime control deterministic by requesting frozen_clock and patching the relevant runtime module, or use fixed_now where the test only constructs expected timestamps.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/unit/devtools/test_schema_lab_commands.py` around lines 355 - 365, Make
test_schema_generate_valid_route_consumes_fresh_gate_receipt deterministic by
requesting the frozen_clock fixture and patching the runtime module’s datetime
source used to evaluate receipt freshness. Set the frozen time consistently with
the authoritative receipt so the test does not depend on wall-clock timing; use
fixed_now only for expected timestamp construction where applicable.
Source: Coding guidelines
| @pytest.mark.parametrize("mutation", ["replaced", "stale"]) | ||
| def test_schema_generate_rejects_replaced_or_stale_receipt( | ||
| monkeypatch: pytest.MonkeyPatch, | ||
| tmp_path: Path, | ||
| mutation: str, | ||
| capsys: pytest.CaptureFixture[str], | ||
| ) -> None: | ||
| root, receipt = _authoritative_schema_gate(tmp_path, mutation) | ||
| payload = json.loads(receipt.read_text(encoding="utf-8")) | ||
| if mutation == "replaced": | ||
| payload["receipt_nonce"] = "attacker-replacement" | ||
| else: | ||
| payload["generated_at"] = datetime(2020, 1, 1, tzinfo=UTC).isoformat() | ||
| payload["receipt_sha256"] = schema_inference_gate_receipt_digest(payload) | ||
| receipt.write_text(json.dumps(payload, sort_keys=True), encoding="utf-8") | ||
| monkeypatch.setattr(schema_generate, "get_config", lambda: _ConfigStub(root / "index.db", root)) | ||
| monkeypatch.setattr(schema_generate, "infer_schema", pytest.fail) | ||
|
|
||
| assert schema_generate.main(["--provider", "chatgpt", "--schema-inference-receipt", str(receipt)]) == 1 | ||
| error = capsys.readouterr().err | ||
| assert "digest mismatch" in error or "stale" in error or "attestation" in error |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
The "stale" case fails on attestation, so the receipt TTL stays untested.
validate_schema_inference_gate_receipt verifies the HMAC attestations at Line 1226 of polylogue/maintenance/schema_inference_gate.py, before the freshness window at Line 1233. The "stale" branch rewrites generated_at and recomputes receipt_sha256, but it cannot recompute the attestation MACs, so verification fails at the attestation step. RECEIPT_TTL and RECEIPT_CLOCK_SKEW are never exercised.
The disjunction at Line 403 hides this. It accepts three different messages, so each parametrized case passes for the wrong reason.
Assert the exact expected failure per case, and cover the TTL through validate_schema_inference_gate_receipt(..., now=...) with a genuinely signed receipt and an advanced now.
🧰 Tools
🪛 ast-grep (0.45.0)
[info] 396-396: use jsonify instead of json.dumps for JSON output
Context: json.dumps(payload, sort_keys=True)
Note: [CWE-116] Improper Encoding or Escaping of Output.
(use-jsonify)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/unit/devtools/test_schema_lab_commands.py` around lines 383 - 403,
Update test_schema_generate_rejects_replaced_or_stale_receipt to assert the
exact expected error for each mutation instead of accepting a disjunction of
messages. Preserve the replaced-receipt case for attestation failure, and test
staleness using a genuinely signed receipt passed to
validate_schema_inference_gate_receipt with an advanced now value beyond
RECEIPT_TTL (while respecting RECEIPT_CLOCK_SKEW), so the freshness-window logic
is exercised directly.
Resolve the authoritative archive root consistently across schema entrypoints, require the legacy writer's signed gate receipt at argument parsing, and make an explicit empty ground-truth mapping stay empty. The expiry assertion validates a real signed receipt at a controlled time, and the shared archive fixture removes test-to-test private imports. Co-Authored-By: Codex <noreply@openai.com>
Summary
Bind schema inference and commit authority to a signed receipt from an external, non-archive ground-truth source.
Problem
The prior gate could accept archive/blob data as its own external ground truth, accept empty evidence, and permit schema commit or the legacy operator CLI to bypass receipt authorization. A receipt hash could also be recomputed after evidence replacement.
Solution
Reject archive-local evidence roots, require nonempty verified evidence, sign the canonical receipt with durable authority material, and require authorized receipts for generation and commit entrypoints. Add mutation-sensitive tests for every bypass path.
Verification
devtools test tests/unit/maintenance/test_schema_inference_gate.pypassed: 74 tests.devtools verify --quickpassed before this branch was published; pre-push records its current quick baseline.Ref polylogue-tnqqt
Summary by CodeRabbit
New Features
Bug Fixes
Tests