Releases: eezz4/zzop
Release list
v0.21.1
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/cliinstalls the same nativezzopCLI binary GitHub Releases ships — identical subcommands, identical output.@zzop/cliis 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/nativeSDK 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
mainnow 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) andperf/jpa-eager-fetch(Java): declaration-level eager loading — TypeORMeager: true, Sequelizeinclude: [{ all: true }], JPAFetchType.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: awaitedfs/fs.promisescalls 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 (includinggo-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/), withcrates/hostreduced 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
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) andzzop-mcp(the MCP stdio server) — thin entries over one shared library. Release assets ship both, 5 platforms each, plus the.mcpbbundles. (Reverses v0.20.0's single-binary direction; npm remains removed.) - Version SSOT. The workspace
Cargo.tomlversion is the single source; both binaries report it viaCARGO_PKG_VERSION, and CI fails the release unless the pushed tag,Cargo.toml, and the plugin manifest all agree. cross-layer/retrying-write-no-idempotencyis 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 theIdempotency-Keyheader) or via an injectedidempotency-guardedattribute (Mode Battributes, exact IoKey or PathScope). Every finding carries a paste-readydata.injectionStub. Suppress per-site with the standard marker if a handler is idempotent in a way neither channel expresses.httppack goes framework-neutral.auth-gatesandroute-exposuremigrated from anapiRoutes.-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-gatesvetoes on theauth-guardedattribute (native middleware/decorator/annotation recognition — produced even whenmutating-route-no-authis disabled — or Mode B injection) and deliberately dropped its same-line keyword carve-out (a lexical token is not auth evidence).read-model-pathstays, 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-okand# marker-ok). - Precision fixes. Next.js
pages/apiverb 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.getas HTTP). Hand-rolled fixed-method wrappers over builtinfetchnow join the cross-layer graph natively (the S7 self-report quiets automatically for them).
Behavior changes to expect
- Findings previously at
warningforcross-layer/retrying-write-no-idempotencyare nowcritical(or vetoed entirely when the provider guard is witnessed). http/auth-gatesandhttp/route-exposurecan fire on trees that never produced them before (any framework with/admin//internalresp. dev/debug-style route segments). Escape hatches: theauth-guardedattribute,// auth-gate-ok/# auth-gate-okmarkers,rules: {"http/auth-gates": "off"}.- Keyword-bearing handler names (
adminHandlers.x,guardedHandlers.y) no longer clearauth-gates; route-level HOF guards (requireAdmin(handler)) currently fire as a known, suppressible false positive until natively recognized. - Next.js
pages/apiroutes may report FEWER verbs (helper-function false verbs removed) or degrade to the serve-all sentinel for HOF-wrapped default exports — disclosed viacross-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
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-routediscloses 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
routesinjection field (AnalyzeRequest+ configtrees[].routes) and paste-readydata.injectionStubstrings 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
v0.18.0
v0.17.0 — Rust & Go native parsers, Java full CST, Python full AST
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.
implmethods/assoc consts),use/moddep graph with module-path + same-workspaceCargo.tomlresolution, axum router provides (builder chains,.nest/.mergecross-file composition),reqwestliteral egress consumes. - Go — Full CST (native, tree-sitter-go 0.25): symbols,
go.mod-resolved dep graph with package-directory-wide edges, gin +net/httprouter provides (route groups, Go 1.22"METHOD /path"mux patterns),net/httpegress 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-fileextends-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,APIRouterprefix,include_routercomposition),requests/httpxegress consumes. Python-2-only syntax degrades to the lexical fallback.
Behavior changes to expect
- One-time full cache rebuild.
CACHE_SCHEMA_VERSIONv21 → 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 withpacksDir: 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), embeddedzzop://contract/*resources 7 → 9, and azzop-mcp contract [<name>]CLI on-ramp that prints any contract without an MCP client. Prebuiltzzop-mcpbinaries 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):validateRulePackOnlyadded (seven exported functions).
Internal
- Guard fleet 12 → 16 CI scripts, including a SIGPIPE seal after a real CI red:
printf big-blob | grep -qunderpipefailinverted 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
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 viaconfigPath— the config'strees, including"auto", define the join — or config-freepathsmode, which explicitly discloses any per-tree config it did NOT load), andvalidate_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, thedisclosureregistry — 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. Seedocs/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 whosesourceIdis"Y"warns that its facts merge intoY— 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[].pathentries that match no file in the tree are counted and named — path typos no longer silently inflatefileCount. - 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 indocs/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):AnalyzeRequestaccepts inline rule-pack definitions, loaded beforepacksDirdirectories (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) andzzop-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 renamedpackages/napi→packages/nativeto match its public name@zzop/native. Crate names are unchanged. - Docs: adapter self-disclosure contract notes (
docs/NORMALIZED_AST.md,docs/adapters/README.md), newdocs/modules/mcp.mdandpackages/mcp/README.md,packDefs/validateEnvelopeOnlydocumented indocs/modules/napi.md, the DSL reference gained the previously-missingtrigger_in_loopfield, plus a number of accuracy fixes (including the site'shealthoutput 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;
packDefsis additive.
v0.15.0
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_fragments → procedure_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). Newexamples/auth-overlay-adapterdemonstrates 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,
--allfor the full text. - Sharper confident-zero severities —
unconsumed-mutation-endpoint/unprovided-mutation-callde-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
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 matchingdirwins per file,
stacking on top of prefixes zzop extracts from code (e.g. Nest
setGlobalPrefix). Applies to Mode A envelope trees too.hosts: consumes likehttps://api.foo.com/usersfrom another tree stop
being external egress and join this tree's routes as/users. New
conditional output fieldcrossLayer.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-consumeaggregates 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 producespath-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-adapterreproduces 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
inraw) 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
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.baseURLpath-prefix keying — a literalaxios.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/apion 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-prefixio kinds gets one aggregate warning per envelope/overlay. In adapter-overlay mode this is also a behavior fix: previously an overlay-emittednest-global-prefixwould re-prefix the whole tree's routes. --jsonstdout is clean again — the "Wrote N reports…" notice moved to stderr (it polluted parseable stdout in v0.12.0).--helpno longer claims "zero-config" (a config file is required;zzop initscaffolds it) and now documents exit codes — scoped honestly: for analysis runs,failOndefaults to"warn"and a first run exiting 1 on findings is expected;zzop adapter validateignoresfailOn(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 exactpack/ruleids: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.jsonregenerated field-for-field against the Rust types (it was one release stale: missing body shapes,loop_spans,is_entry,client; wrong required-ness on fourOptionfields;sourceSymbolcasing) 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);overlaysandgit.sinceare 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