Skip to content

Releases: eezz4/zzop

v0.21.1

Choose a tag to compare

@github-actions github-actions released this 23 Jul 13:32

v0.21.1 — npm distribution revived (zero-logic), product-layer restructure, new perf/concurrency rules, reachability contract

This release brings the npm CLI back as a thin native-binary shim, restructures the workspace into a clean product/library split, adds declaration-level eager-loading rules plus concurrency-loop rules, and machine-pins parser↔rule reachability. No public runtime behavior of the analysis engine changed except the new rules and one Go extraction fix.

npm distribution is back — as zero-logic packaging (6 packages)

  • npm i -g @zzop/cli installs the same native zzop CLI binary GitHub Releases ships — identical subcommands, identical output. @zzop/cli is a ~90-line launcher that spawns the platform binary carried by its @zzop/cli-<platform> sub-packages; there is no separate JS implementation to drift from the native binary (the pre-0.20 JS CLI dialect is gone for good).
  • The 0.20 npm removal was reversed; the napi @zzop/native SDK was not revived (nothing in the distribution used it — programmatic Node use = run the CLI with its JSON output).
  • Publishing uses npm OIDC trusted publishing (no stored token) and is resumable.

Release pipeline: secretless auto-tagging + official MCP registry

  • A workspace version bump landing on main now auto-tags and releases itself — CI reads the Cargo version, creates the tag, and the same run ships the release. No manual tag step.
  • zzop's MCP server is now auto-published to the official MCP registry (registry.modelcontextprotocol.io, io.github.eezz4/zzop) via GitHub OIDC — discovery for the non-Claude ecosystem. Usage still needs no registration (a stdio config suffices).

New rules (4)

  • perf/eager-relation-declared (TS/JS) and perf/jpa-eager-fetch (Java): declaration-level eager loading — TypeORM eager: true, Sequelize include: [{ all: true }], JPA FetchType.EAGER — the query-time opposite of N+1 (over-fetch on every read). Verified against real corpora with zero false positives.
  • be-db/tx-in-loop-long-hold: a transaction wrapping a per-row loop, so lock hold time scales with row count.
  • be-reliability/fs-in-loop-serial: awaited fs/fs.promises calls serialized inside a loop.

Reachability contract + a Go extraction fix it caught

  • A new parser×rule capability-matrix meta-test machine-pins which parser environment projects which channels (symbols, method/loop spans, IO facts) and fails any rule whose file pattern would make it forever-silent — the "which languages/DBs can a rule actually reach" facts are no longer prose.
  • Building it surfaced a real bug: Go functions whose body opened with a // comment lost their body span, silently suppressing method/loop-scan rules (including go-goroutine-in-loop) on them. Fixed, with a same-class audit across every parser.

Internal

  • Workspace restructured into a product layer (packages/{cli,cli-bin,mcp,mcpb}) over an internal library layer (crates/), with crates/host reduced to the shared library both binaries link. Binary names, release assets, and npm package names are unchanged.

Cache note: the Go parser fingerprint bumped, so Go trees re-analyze once on first run after upgrade; all other caches survive.

v0.21.0 — two binaries, framework-neutral security rules, and the first CRITICAL cross-layer finding

Choose a tag to compare

@eezz4 eezz4 released this 22 Jul 16:34

v0.21.0 — two binaries, version-stamped caches, framework-neutral security rules, and the first CRITICAL cross-layer finding

Highlights

  • Two binaries per release. zzop (the plain CLI: analyze, cross, endpoint, analyze-envelope, validate-envelope, validate-rule-pack, contract, version) and zzop-mcp (the MCP stdio server) — thin entries over one shared library. Release assets ship both, 5 platforms each, plus the .mcpb bundles. (Reverses v0.20.0's single-binary direction; npm remains removed.)
  • Version SSOT. The workspace Cargo.toml version is the single source; both binaries report it via CARGO_PKG_VERSION, and CI fails the release unless the pushed tag, Cargo.toml, and the plugin manifest all agree.
  • cross-layer/retrying-write-no-idempotency is now CRITICAL and two-sided. A retry-configured frontend write joined to a real provider route fires only when no idempotency guard is witnessed on that provider: natively (an inline handler reading the Idempotency-Key header) or via an injected idempotency-guarded attribute (Mode B attributes, exact IoKey or PathScope). Every finding carries a paste-ready data.injectionStub. Suppress per-site with the standard marker if a handler is idempotent in a way neither channel expresses.
  • http pack goes framework-neutral. auth-gates and route-exposure migrated from an apiRoutes.-only line scan to whole-tree io-scan over ASSEMBLED provides — they now fire on Express, Hono, NestJS, Spring, ASP.NET Core, FastAPI, Django, gin, axum routes alike. auth-gates vetoes on the auth-guarded attribute (native middleware/decorator/annotation recognition — produced even when mutating-route-no-auth is disabled — or Mode B injection) and deliberately dropped its same-line keyword carve-out (a lexical token is not auth evidence). read-model-path stays, explicitly reframed as a house-convention rule.
  • DSL io-scan matcher grew up. Evaluates whole-tree (it now sees router-mount/controller-prefix/file-convention/whole-corpus composed routes), and gained symbol_pattern, attr_present/attr_absent, anchor_exclude_pattern, file_exclude_pattern, plus suppress-marker support (// marker-ok and # marker-ok).
  • Precision fixes. Next.js pages/api verb detection is handler-body-scoped with the real request parameter name (helper-function mentions no longer mint verbs). Python instance-egress bindings are last-write-wins (a client variable reassigned to a dict no longer misreads .get as HTTP). Hand-rolled fixed-method wrappers over builtin fetch now join the cross-layer graph natively (the S7 self-report quiets automatically for them).

Behavior changes to expect

  • Findings previously at warning for cross-layer/retrying-write-no-idempotency are now critical (or vetoed entirely when the provider guard is witnessed).
  • http/auth-gates and http/route-exposure can fire on trees that never produced them before (any framework with /admin//internal resp. dev/debug-style route segments). Escape hatches: the auth-guarded attribute, // auth-gate-ok / # auth-gate-ok markers, rules: {"http/auth-gates": "off"}.
  • Keyword-bearing handler names (adminHandlers.x, guardedHandlers.y) no longer clear auth-gates; route-level HOF guards (requireAdmin(handler)) currently fire as a known, suppressible false positive until natively recognized.
  • Next.js pages/api routes may report FEWER verbs (helper-function false verbs removed) or degrade to the serve-all sentinel for HOF-wrapped default exports — disclosed via cross-layer/unknown-verb-route, never silently mis-keyed.
  • Every cache re-parses cold once after upgrading: all cache-bust tokens moved to package-version stamps (<scope>/<last-change-version>). Subsequent releases that leave a scope untouched will keep its warm cache — that is the new scheme's point.
  • Custom packs using io-scan rules: those findings now evaluate whole-tree (composed provides visible, attributes consultable) and honor suppress markers; they no longer appear in per-file cached findings.

Corpus check (RealWorld pairs)

No regressions: Angular×Spring 19 edges, axios×ASP.NET 19, axios×FastAPI 19, Vite×Django unchanged; Vite×Express improved 39 → 48 joined edges vs v0.20.0 (builtin-fetch wrapper joins + earlier fixes). New io-scan rules fire on zero corpus trees (no admin/debug paths there) — verified against synthetic e2e fixtures instead.

v0.20.0

Choose a tag to compare

@github-actions github-actions released this 21 Jul 14:22

v0.20.0

Routing is injection, end-to-end

Routes are resolved from visible code literals on two axes — path and HTTP verb. When either axis is dynamic, zzop now marks an injection boundary instead of fabricating a route:

  • The old [GET, POST] verb fabrication is gone → cross-layer/unknown-verb-route discloses the unknown verb.
  • A non-literal route path is dropped honestly (tri-state), never collapsed into a phantom route at an empty base.
  • Java and C# now resolve non-literal method-path / class-prefix constants across the whole corpus.
  • New lightweight routes injection field (AnalyzeRequest + config trees[].routes) and paste-ready data.injectionStub strings close the last mile: inject the missing route fact and the cross-layer join resolves.

Native C# (ASP.NET Core)

New tree-sitter frontend: symbols/imports/namespaces, attribute-routed + minimal-API HTTP provides, HttpClient egress consumes, namespace dependency graph, and a whole-corpus route-constant resolver (partial classes are merged, not dropped).

ORM db-table channel complete — 5 producers

Prisma/SQL DDL, TypeORM @Entity, GORM, SQLAlchemy/SQLModel, and Django all mint and consume the db-table join channel under one canonical key.

Framework auth-route modeling

mutating-route-no-auth now models Spring method-security + global SecurityFilterChain and NestJS forRoutes middleware; route-shadowing fires only on first-match routers (framework-scoped). Environment gates (isLocal / isProduction / NODE_ENV) are no longer mistaken for authorization.

Multi-verb route expansion

axum any(), express/hono .all, gin .Any, and FastAPI @api_route(methods=[...]) each expand to one route per verb.

Distribution: npm removed

@zzop/cli and @zzop/native are gone — the zzop-mcp binary is the single runtime form. An MCPB manifest ships for Claude Desktop, and validate-envelope / validate-rule-pack CLI subcommands were added.

Behavior changes to expect

Parser fingerprints bumped for C#, Go, Java, Python, Rust, SQL, and TypeScript (route-axis + db-table + egress work), so those languages' cached per-file results recompute once. C#, the TypeORM/GORM/SQLAlchemy/SQLModel/Django db-table channel, and framework-auth findings are new. Verb-unknown routes now disclose instead of fabricating GET/POST. No npm packages are published.

Full Changelog: v0.19.0...v0.20.0

v0.19.0

Choose a tag to compare

@github-actions github-actions released this 18 Jul 07:35

Full Changelog: v0.18.0...v0.19.0

v0.18.0

Choose a tag to compare

@github-actions github-actions released this 17 Jul 20:53

Full Changelog: v0.17.0...v0.18.0

v0.17.0 — Rust & Go native parsers, Java full CST, Python full AST

Choose a tag to compare

@github-actions github-actions released this 16 Jul 21:24

Three new full-precision native parsers in one release: Rust and Go join as new languages, Java is promoted from lexical to full CST, and Python's full-AST tier (ruff-based) ships. The MCP host grows to 5 tools / 9 embedded contract resources, and the engine now tells you exactly what it could not parse — and how to close the gap with a minimal adapter.

New languages / tiers

The canonical per-language table is docs/ARCHITECTURE.md → Language support.

  • Rust — Full AST (native, syn 2): symbols (incl. impl methods/assoc consts), use/mod dep graph with module-path + same-workspace Cargo.toml resolution, axum router provides (builder chains, .nest/.merge cross-file composition), reqwest literal egress consumes.
  • Go — Full CST (native, tree-sitter-go 0.25): symbols, go.mod-resolved dep graph with package-directory-wide edges, gin + net/http router provides (route groups, Go 1.22 "METHOD /path" mux patterns), net/http egress consumes (incl. fmt.Sprintf-reassembled paths). An ERROR CST region is never guessed past.
  • Java — lexical → Full CST (native, tree-sitter-java 0.23.5, Java 21 grammar): nested-type symbols with body spans, plain/glob/static import resolution via an in-tree (package, type) index, Spring MVC provides with cross-file extends-chain + constant-prefix resolution. Real-corpus delta: dep-graph edges 0 → 342 on the Spring RealWorld backend.
  • Python — Full AST (native, ruff parser crates — no Python runtime needed): def/class/method symbols with __all__-aware exports, relative-import dep graph, FastAPI provides (decorators, APIRouter prefix, include_router composition), requests/httpx egress consumes. Python-2-only syntax degrades to the lexical fallback.

Behavior changes to expect

  • One-time full cache rebuild. CACHE_SCHEMA_VERSION v21 → v25 (shared projected types grew for the new parsers) — the first run after upgrading re-analyzes everything once. Parser fingerprints also bumped: TypeScript gains +express-middleware-v1; the python3/rust/go/java21 fingerprints are new.
  • Envelope trees now load the bundled DSL packs by default (previously zero packs unless configured). The reply discloses packsLoaded; opt out with packsDir: null. All 112 bundled rules are line/method-scan, so structural findings are unchanged — envelope trees may simply gain text-level findings they were silently missing.
  • Un-parsed files are disclosed, not silent: per-extension "no native parser" diagnostics (58 known non-source extensions exempt), overlay source-mismatch / synthetic-entry / zero-fact census, and coverage-gap warnings that chain into building a minimal partial Mode-B adapter envelope — a tens-of-lines script (a 91-line Java example ships in examples/).

New surface

  • MCP host (zzop-mcp): tools 3 → 5 (analyze_repo, cross_repo, check_endpoint, validate_envelope, validate_rule_pack), embedded zzop://contract/* resources 7 → 9, and a zzop-mcp contract [<name>] CLI on-ramp that prints any contract without an MCP client. Prebuilt zzop-mcp binaries for 5 platforms are attached to this release (first release shipping them).
  • CLI: zzop endpoint <method> <path> — is this endpoint linked/provided/consumed? 8 explicit verdicts; honors --config, --json. zzop pack validate <file> — lint a DSL rule pack's structure before loading it.
  • SDK (@zzop/native): validateRulePackOnly added (seven exported functions).

Internal

  • Guard fleet 12 → 16 CI scripts, including a SIGPIPE seal after a real CI red: printf big-blob | grep -q under pipefail inverted a genuine match once the input passed the pipe buffer — all sites converted to herestrings, and the pattern is now machine-banned.
  • 300-line ratchet baseline burned down to 0 grandfathered files.
  • Release gated by the 3-lens audit (policy values / message honesty / boundary pins) + live-corpus smoke: Angular × Spring cross-layer edges hold at 19, zero tripwire false-fires, 3280 workspace tests green.

No breaking API changes. analyze/analyzeTrees request/response shapes are unchanged; new output is additive (warnings, packsLoaded, new endpoints/tools).

v0.16.0 — Node-free MCP host, overlay self-disclosure, "bring an adapter" diagnostics

Choose a tag to compare

@github-actions github-actions released this 15 Jul 18:13

v0.16.0 — Node-free MCP host, overlay self-disclosure, "bring an adapter" diagnostics

Highlights

zzop-mcp: a Node-free host binary (in-tree). One self-contained executable that runs the full engine with zero Node runtime — an MCP stdio server plus direct CLI subcommands.

  • Tools: analyze_repo (auto-discovers <path>/zzop.config.jsonc; zero-config runs still include the bundled rule packs and default git signal collection), cross_repo (config-first via configPath — the config's trees, including "auto", define the join — or config-free paths mode, which explicitly discloses any per-tree config it did NOT load), and validate_envelope (the custom-parser authoring feedback loop).
  • Resources: 7 authoring contracts embedded in the binary under zzop://contract/* — envelope JSON Schema and guide, the byte-pinned key-normalization fixture, DSL reference and authoring guide, a minimal example envelope. You can author a custom parser or rule pack against the binary alone, with no source checkout.
  • Output contract: summary-first (full counts by severity/rule always ride along), capped lists always disclose truncation ({shown, totalMatching, hint}), and the honesty channels — warnings, configWarnings, the disclosure registry — are never capped.
  • Protocol robustness: malformed JSON-RPC lines answer -32700 (previously a silent swallow that could hang a client on Windows-path escapes); non-object frames answer -32600.
  • Distribution status: build from source this release (cargo build -p zzop-mcp); prebuilt binaries are the next distribution step. See docs/modules/mcp.md.

Overlay self-disclosure (engine). Mode-B adapter overlays now explain themselves instead of failing silently:

  • source-mismatch: an overlay declaring source: "X" attached to a tree whose sourceId is "Y" warns that its facts merge into Y — joins that look cross-source read as intra-source. (Emitted only when the overlay carries join-relevant io; attribute-/entry-marker-only overlays are source-agnostic and stay quiet.)
  • synthetic entries: declared files[].path entries that match no file in the tree are counted and named — path typos no longer silently inflate fileCount.
  • zero-fact envelopes: an overlay whose entries carry no consumed facts is called out — and, with teeth: an overlay entry now counts as parser coverage only if it actually contributes a fact, so an empty adapter can no longer mask the diagnostic below. (Note for adapter authors: Mode B does not consume overlay symbols; see the updated contract notes in docs/NORMALIZED_AST.md.)

Per-extension "bring an adapter" diagnostic (engine). Files whose extension has no native parser (a .sql migration, a .py service) no longer vanish silently: one aggregated warning per extension names sample files and the exact remedy (overlays: [...] in zzop.config.jsonc). Non-source files (docs, images, lockfiles, styles, ...) are excluded via a pinned list.

Also in this release

  • packDefs (embedders): AnalyzeRequest accepts inline rule-pack definitions, loaded before packsDir directories (a directory pack with the same id wins) — this is how the self-contained binary ships its bundled packs as data. The JS surface is unchanged.
  • New shared crates: zzop-facade (the pure JSON facade, extracted so the Node addon and the binary drive one engine code path) and zzop-config (a Rust port of the config front-end with JS parity; one documented deviation — path-like config values resolve against the config file's directory, since a server host's cwd is meaningless).
  • Workspace layout (contributors): internal crates moved under crates/; packages/ now holds only published artifacts; the npm binding directory was renamed packages/napipackages/native to match its public name @zzop/native. Crate names are unchanged.
  • Docs: adapter self-disclosure contract notes (docs/NORMALIZED_AST.md, docs/adapters/README.md), new docs/modules/mcp.md and packages/mcp/README.md, packDefs/validateEnvelopeOnly documented in docs/modules/napi.md, the DSL reference gained the previously-missing trigger_in_loop field, plus a number of accuracy fixes (including the site's health output example, which showed a field that does not exist).

Behavior changes to expect

  • New warnings may appear on existing repos — the unparsed-extension diagnostic and the overlay self-disclosure set. These are warnings only: findings, caches, and parser fingerprints are unchanged, so upgrading does not trigger a re-analysis.
  • No breaking changes. The envelope contract (v1) and every config key are unchanged; packDefs is additive.

v0.15.0

Choose a tag to compare

@github-actions github-actions released this 15 Jul 09:49

zzop v0.15.0 — the generic entity-attribute injection channel, concern-first rule packs.

⚠️ Breaking changes

Rule-pack ids (config rules / disabledRules / suppressions / packs.disabled). An unmatched override is a silent no-op, so update these mechanically:

Old id New id
java-security/sql-taint be-security/sql-taint
java-security/weak-crypto be-security/weak-crypto
java-security/cmd-injection be-security/cmd-injection
be-security/java-hardcoded-password be-security/hardcoded-password
be-security/java-weak-random be-security/weak-random
be-security/java-path-traversal unchanged (kept — collides with the JS path-traversal rule)

Remove java-security / jsp-security from packs.disabled (the packs no longer exist; jsp-security shipped no rules). Inline // ...-ok suppress markers in source are unchanged.

Envelope contract (Mode A/B adapter authors only — not CLI/config users): trpc_router_fragmentsprocedure_router_fragments (and the matching schema/type names). The shape is identical and the join kind="trpc" value is unchanged; an adapter still emitting the old field is silently ignored until renamed.

Behavior change to expect

One-time cache rebuild on upgrade — the TypeScript parser fingerprint bumped (+store-binding-removed-v1) and the cache schema is now v21. The first run after upgrading re-analyzes every file from scratch (old cache entries no longer match); subsequent runs are warm again. No findings change as a result of the bump itself.

New

  • Generic entity-attribute injection channel. Adapters can inject cross-cutting facts the per-file analysis can't see — router-level middleware auth, model store-bindings, migration churn — through an open-vocab attributes: [{ target, key, value }] channel (target = file | symbol | ioKey | pathScope). First consumers: mutating-route-no-auth (auth-guarded), dead-model (bound-model), schema-churn (model-churn). New examples/auth-overlay-adapter demonstrates Mode-B middleware-auth injection.
  • trees: "auto" — opt-in workspace auto-detect for cross-repo join discovery.
  • One-line terminal message folding — a headline per finding, --all for the full text.
  • Sharper confident-zero severitiesunconsumed-mutation-endpoint / unprovided-mutation-call de-escalate to Info when the join is blind on the opposite side (a "zero" the analysis can't actually vouch for).

Removed (over-reach → injection)

App-specific native store-binding and migration-churn recognizers are gone; both are now supplied through the attribute channel (bound-model / model-churn), keeping the native engine to common environments only.

v0.14.0 — deployment topology & field-driven precision

Choose a tag to compare

@github-actions github-actions released this 14 Jul 14:03

v0.14.0 — deployment topology & field-driven precision

zzop now lets you declare the one class of cross-repo join information that
lives in no repo's code: deployment topology. Plus field-driven precision
fixes that cut real-corpus noise, and the oazapfts recognizer moves out of
the engine into a reference adapter.

New: connection topology config (mountedAt / mounts / hosts)

Per-tree keys in zzop.config.jsonc declaring deployment facts:

"trees": [
  { "root": "../web-fe", "sourceId": "web" },
  { "root": "../api-be", "sourceId": "api",
    "mountedAt": "/api",                              // whole-tree gateway mount
    "mounts": [{ "dir": "apps/settle", "at": "/settle" }], // monorepo fragmentation
    "hosts": ["api.foo.com"] }                         // absolute-URL calls join this tree
]
  • mountedAt / mounts: prepend a gateway prefix to a tree's (or a
    sub-directory's) HTTP provide keys — longest matching dir wins per file,
    stacking on top of prefixes zzop extracts from code (e.g. Nest
    setGlobalPrefix). Applies to Mode A envelope trees too.
  • hosts: consumes like https://api.foo.com/users from another tree stop
    being external egress and join this tree's routes as /users. New
    conditional output field crossLayer.hostRekeyCounts (omitted when no
    hosts are declared) reports per-host effect.
  • Honesty built in: a mount or host with zero effect warns (stale config
    self-discloses); path-shape errors fail config loading; values are literal
    paths, not rewrite patterns.

Behavior changes to expect

  • unprovided-consume aggregates partial-provider noise: in a tree that
    provides only part of its own key space (e.g. a monorepo where one app's
    routes are visible), unmatched consumes whose first path segment lies
    outside the provided space now fold into ONE info finding (at 3+),
    enumerating every key (data.callCount / data.routes). Consumes that
    overlap the provided space keep today's individual findings (the typo
    signal). Field result: 9 findings → 1 on a real monorepo.
  • Near-miss rules gate zero-information keys: a consume key whose
    segments are all {} placeholders (e.g. `${base}/${x}` keying as
    GET /{}) no longer produces path-near-miss/route-near-miss
    suggestions — an all-slot key "resembles" every same-length route, so the
    suggestion carried no information. Declared catch-all routes remain
    legitimate suggestion targets. Field result: 4 duplicate suggestions → 0.
  • oazapfts recognition moved out of the engine (generated SDKs =
    injection adapters): the native recognizer (fetchJson/fetchText/fetchBlob,
    oazapfts.<helper>() method unwrap, trailing ${QS...} drop) is removed;
    examples/oazapfts-adapter reproduces it as a Mode B overlay adapter.
    An oazapfts import with no visible consumes is now disclosed by
    sdk-import-no-visible-consume ("inject an adapter"). A trailing
    ${QS...} interpolation now keys as an ordinary {} placeholder.
  • PARSER_FINGERPRINT bump (+oazapfts-removed-v1): first run after
    upgrading re-extracts TypeScript files (cache recompute); cache schema
    itself is unchanged (v20).
  • Re-keyed host consumes carry their join key (original spelling preserved
    in raw) in the cross-layer buckets.

Docs

Public docs now lead with what zzop is: deterministic cross-repo contract
analysis you can gate CI on — same code in, same findings out. Rule packs
are documented as the supporting engine. Rule catalog rows updated for the
behavior changes above; AnalyzeRequest docs gained the topology fields.

v0.13.0 — contract honesty: body-field-drift, baseURL keying, silent no-op sweep

Choose a tag to compare

@github-actions github-actions released this 13 Jul 14:53

Highlights

  • New cross-layer rule: cross-layer/body-field-drift — a frontend request-body literal is checked against the backend @Body() DTO's declared fields on every joined route; mismatches (a body key the DTO does not declare, a missing required field, a missing wrapper key) are reported. Never-guess tone: optional-field omission and backend whitelist-stripping are NOT flagged — only witnessed literal drift.
  • axios.defaults.baseURL path-prefix keying — a literal axios.defaults.baseURL = "/api" (or a host-carrying base with a path part) now prepends its path to that client's consume keys before the cross-layer join. Findings will move: pairs that agree on /api on both sides gain real joins; pairs whose backend does not carry the prefix now honestly report prefix drift instead of accidentally joining. Scope: literal bases only — interceptors, wrappers, non-literal constants and generated clients remain adapter-injection territory.

Behavior changes to expect

  • Cache recompute (one-time): cache schema bumped v18 → v20 (body-shape capture + client-base keying changed artifact payloads). The first run after upgrading re-extracts everything.
  • New warnings for silent config no-ops — config entries that previously did nothing quietly now say so: unknown rule ids in severity overrides and suppressions (joining the existing disabled-rules warning). Each warning names the actual config dialect (rules: { "<id>": ... } / packs.disabled) and the embedder field, so the advice is actionable in both dialects.
  • Reserved sentinel kinds are now dropped with a warning in both envelope modes — an external producer emitting the engine-internal nest-global-prefix / client-base-prefix io kinds gets one aggregate warning per envelope/overlay. In adapter-overlay mode this is also a behavior fix: previously an overlay-emitted nest-global-prefix would re-prefix the whole tree's routes.
  • --json stdout is clean again — the "Wrote N reports…" notice moved to stderr (it polluted parseable stdout in v0.12.0).
  • --help no longer claims "zero-config" (a config file is required; zzop init scaffolds it) and now documents exit codes — scoped honestly: for analysis runs, failOn defaults to "warn" and a first run exiting 1 on findings is expected; zzop adapter validate ignores failOn (its exit 1 means the envelope failed structural validation).

Docs corrections (act if you copied old examples)

  • Every rules: example in older docs used bare DSL ids ("no-explicit-any", "n-plus-one", "toctou") — those entries were silent no-ops. The engine matches exact pack/rule ids: typescript/no-explicit-any, sql/nplus1, sql/race-condition-toctou. All examples are corrected, a CI guard now validates doc example ids against the catalog, and the new unknown-id warnings will flag stale entries in your config at run time.
  • envelope.schema.json regenerated field-for-field against the Rust types (it was one release stale: missing body shapes, loop_spans, is_entry, client; wrong required-ness on four Option fields; sourceSymbol casing) and is now machine-pinned by a bidirectional parity test (names, required, nullability, enums).
  • NORMALIZED_AST: casing contract, reserved-kinds section, absolute-URL (://) verbatim rule, key-normalization crosslinks. ARCHITECTURE: join-integrity gates (cross-tree ambiguity is not auto-linked; :// routes to externalConsumes; low-confidence joins are tagged), sentinel shims, and the user-facing baseURL keying consequence.
  • Report dir is zzop-reports/zzop.<epoch> (Markdown by default, written by default); overlays and git.since are now documented config keys.

Internal quality

Docs rule-id guard, schema parity guard, marker -ok convention pin, disable-hint id-coupling pin, and the CLI unit tests (165+) are all wired into CI. A pre-tag 3-lens release audit (policy values / messages / boundaries) ran on this release; all four confirmed findings were fixed before tagging.

Full Changelog: v0.12.0...v0.13.0