Skip to content

seal verify

seal verify reads seal.json, hashes every recorded bundle and every directory or regular file matched by discovery, and reports whether the project is Verified, Drift, Warning, or Blocked.

It is read‑only: it never prompts, never writes to seal.json, and never makes network calls. Run it as often as you like.

Terminal window
seal verify [--json | --quiet] [--verbose]
Flag Effect
--json Emit a machine‑readable JSON object to stdout. Suppresses the human summary on stderr.
--quiet No output to either stream. Exit code is the only signal.
-v, --verbose In human mode only: expand each Mismatch into per‑file modified / added / missing lines.

--json and --quiet are mutually exclusive. Combining them is a usage error (exit 2).

  1. Locate seal.json in the current working directory. Not found → exit 2 with seal: verify: seal.json not found in current directory; run 'seal init' first.
  2. Decode and validate seal.json. Invalid → exit 2 with the specific validation failure (e.g., unknown field, malformed hash, mismatched contentHash, uncovered bundle key, nested bundle keys, or overlapping discovery patterns).
  3. Build the verification set: every entry in bundles plus every directory or regular file matched by discovery expansion. Unsupported discovery matches, such as symlinks, are kept so they can report Mismatch.
  4. For each set member, hash the on‑disk sealed root and classify it as Verified, Unverified, Removed, or Mismatch. See verification outcomes for details.
  5. Apply policy to derive the overall outcome.
  6. Render output according to the flags.
  7. Exit 0 for Verified / Drift / Warning, 1 for Blocked.

A short summary lists:

  • Counts: <n> verified, <m> unverified, <p> removed, <q> mismatch.
  • Per‑bundle status lines for non‑verified entries.
  • For Mismatch, with --verbose: per‑file lists of modified, added, missing.
  • A trailing line stating the outcome and policy: Result: Blocked (block mode).

The current human renderer is plain text. Treat it as terminal output for people, not as a stable machine interface.

{
"status": "Blocked",
"verified": ["./.claude/skills/code-review"],
"unverified": ["./.claude/skills/new-thing"],
"removed": ["./.claude/skills/deleted"],
"mismatch": ["./.claude/skills/deploy-guard"]
}

Guarantees:

  • status is exactly one of "Verified", "Drift", "Warning", "Blocked".
  • The four arrays (verified, unverified, removed, mismatch) are always present, even when empty.
  • Each array is sorted lexicographically by UTF‑8 bytes (NFC).
  • Implementations MAY include additional diagnostic metadata, but MUST preserve the four required arrays and the status field exactly.

If the CLI exits 2 (fatal error), it does not emit JSON to stdout. Diagnostic text always goes to stderr.

No output to either stream. The exit code is the entire signal, useful for the simplest possible CI gate.

Code Outcome Meaning
0 Verified, Drift, or Warning Project may load bundles.
1 Blocked Unverified or mismatched bundles found under policy: "block".
2 (fatal) seal.json missing or invalid, system error, or --json + --quiet combined.

See the complete exit codes table.

The verification set is exactly:

  • Every entry in bundles.
  • Every directory or regular file matched by discovery pattern expansion.

The CLI does not scan the project tree for bundles outside of discovery. If you want a bundle to be checked, either pin it explicitly with seal pin <path> or add a discovery pattern that produces its bundle key.

Targeted seal pin <path> adds exact discovery coverage for that named key when needed. Exact coverage keeps the lockfile valid, but it does not detect new sibling files. For sibling insertion detection, use a broader pattern such as docs/*.md or pin the containing directory as one recursive sealed root.

This is deterministic across implementations: seal verify in CI on Linux behaves identically to seal verify on a developer’s macOS laptop, because both rely strictly on the written lockfile.

Default (human output to stderr):

Terminal window
$ seal verify
1 verified, 1 unverified, 0 removed, 1 mismatch
Mismatch ./.claude/skills/deploy-guard
Unverified ./.claude/skills/new-thing
Result: Blocked (block mode)
$ echo $?
1

CI gate (silent, exit code only):

Terminal window
$ seal verify --quiet ; echo $?
1

Machine‑readable JSON for downstream tooling:

Terminal window
$ seal verify --json
{
"status": "Blocked",
"verified": ["./.claude/skills/code-review"],
"unverified": ["./.claude/skills/new-thing"],
"removed": [],
"mismatch": ["./.claude/skills/deploy-guard"]
}

Verbose mode with per‑file diffs:

Terminal window
$ seal verify --verbose
0 verified, 0 unverified, 0 removed, 1 mismatch
Mismatch ./.claude/skills/deploy-guard
modified: SKILL.md
added: scripts/new-tool.sh
missing: scripts/old-tool.sh
Result: Blocked (block mode)
You see What it means What to do
Result: Verified All clean. Nothing.
Result: Drift Lockfile lists bundles not on disk. If intentional, seal pin --prune to drop them. Otherwise restore from version control.
Result: Warning Unverified/mismatched under policy: "warn". Loads still allowed. Review the listed bundles; consider switching to policy: "block" once stable.
Result: Blocked Unverified/mismatched under policy: "block". Loads forbidden. Run seal pin <path> after reviewing the actual file changes (git diff).

The discipline Seal asks of you is to look at the diff before re‑pinning. That is the entire point.