v1.53.0
Package Changelog
Minor Changes
-
eddfc6b: The anchor ladder's
executabletier is now reachable in a real settle (Phase
241,rec-20260729-002/rec-20260729-007). Phase 235 shipped the full
four-tier ladder as a pure resolver, but its top rung was dead in production:
SettleContextexposed no prior-gate provenance to aGateImpl— the
accumulator was a local insiderunSettleGates— sogates/code-review.ts
called the resolver with a literal[]and every live finding capped at
structured/declared/undeclared.executablewas exercised only by unit
tests that injected provenance directly.SettleContextgains an optional, readonlygateProvenance— the entries
recorded so far this settle, inGATE_ORDER.runSettleGateshands each
gate a per-gate context carrying a two-level-frozen snapshot: the array
and each entry are frozen, and the entries are copies. The element-level
freeze is the load-bearing half — a shallow copy would leave entries sharing
object identity with the live accumulator, so a gate could have rewritten an
entry that lands inSUMMARY.json.gatesand feeds the phase-233 assurance
record. The field is typedreadonly Readonly<GateProvenance>[], so the
compiler refuses element mutation and the runtime copy holds even against a
gate that casts the guard away. (A plainreadonly T[]would not suffice: it
constrains the array's shape, not its elements' fields.)- The field is optional and additive: every pre-existing
SettleContext
literal, in production and in tests, compiles unchanged, and a reader treats
an absent field the same as an empty array — never as "unknown". gates/code-review.tspasses that snapshot through. Becausecode-review
runs 9th inGATE_ORDERandbuild-test-must-pass5th, the corroborating
status is already recorded by the time anchoring happens.
This widens what is reachable without weakening what must be earned.
The ladder's two-condition check inverify/anchor.tsis untouched:
executablestill requires both an AC cited by a task with a non-empty
verify:and abuild-test-must-passentry withstatus: 'ran'. A
skipped,refused, or absent entry still caps the tier — a failing suite
waved through with--allow-failing-buildrecordsskippedand demonstrably
cannot buy a stronger anchor.Reachability is proven end-to-end rather than asserted: a new test drives the
real CLI over an ephemeral repo at a profile whose gate set includes
code-reviewand reads the tier back out of the persistedSUMMARY.json.
Reverting the one-line gate change flips that recorded tier fromexecutable
tostructured, so the test measures the production path and nothing else.Two limitations disclosed with the ladder in phase 235 remain open and are
still documented: anchoring is resolved per-file rather than per-finding, so an
uncovered defect in an otherwise-covered file can be missed
(rec-20260729-003); and a boundary string that merely contains a finding's
filename as a substring can mask a real gap by grantingdeclaredtier too
broadly (rec-20260729-005). -
c27bcb0:
code-reviewfindings are now criteria-anchored (Phase 235,rec-20260727-004
/rec-20260727-005): every finding is tagged with how strongly it ties back
to something the phase's DRAFT actually declared, on a four-tier ladder —
executable>structured>declared>undeclared— resolved by a new
pureresolveAnchor(packages/core/src/verify/anchor.ts). A finding whose
best anchor resolves toundeclaredis a criteria gap: diff work no
acceptance criterion and no boundary covers.GateProvenanceZ-adjacentSummaryZgains an additiveAnchorZpeer schema
({ kind: 'ac' | 'boundary' | 'none', ref?, tier }, deliberately independent
of the existingAcEvidenceZladder — the two rank different things) and
FindingZgains an optionalanchorfield. Both are purely additive: a
pre-phase-235SUMMARY.jsonwith noanchoron any finding still parses
unchanged.A criteria gap adds no new refusal path and no new bypass flag — a gap
finding flows into the exact same finding streamcode-reviewalready
refuses on, so a HIGH-severity gap refuses through the pre-existing
HIGH-finding contract (dec-20260729-005); gap count and severity
distribution are declared to stderr unconditionally, independent of whether
the gate passes, refuses, or is bypassed (dec-20260729-006).GATE_ORDER
and every gate's pass/refuse semantics for pre-existing finding classes are
unchanged. Scope is deliberately narrow — onlycode-reviewis
criteria-anchored;spec-review,ui-spec-review, andplan-revieware
untouched (dec-20260729-003).Three limitations were filed rather than papered over. The first —
executablenot being reachable in a real settle, becauseSettleContext
exposed no prior-gate provenance to a single gate (rec-20260729-002) — is
resolved by phase 241 in this same release, so it never reaches a
published version; see that entry for the fix. The other two remain open:
anchoring is resolved per-file rather than per-finding, so an uncovered
defect in an otherwise-covered file can be missed (rec-20260729-003); and a
boundary string that merely contains a finding's filename as a substring can
mask a real gap by grantingdeclaredtier too broadly (rec-20260729-005). -
5cc4085: Findings now carry a stable identity (Phase 236,
rec-20260727-006):FindingZ
gains additiveid,target: 'artifact' | 'verification',disposition: 'open' | 'accepted' | 'waived' | 'fixed' | 'superseded', andwaiver: { expiry }
fields.idis a pure content hash over(file, normalized message)—
deliberately never a line number, so the same finding keeps the sameid
across settles even after an unrelated edit shifts which line it sits on
(packages/core/src/verify/finding-identity.ts).anchor/severityare
accepted as parameters for call-site compatibility but do not participate in
the hash (Phase 245 narrowed the formula from an original(file, anchor.kind, anchor.ref, severity, normalized message), after independent review found
both anchor and severity can legitimately change across settles for the same
underlying defect). A
waiveris only valid whendisposition === 'waived', enforced by a
cross-field schema refine — a waiver with no expiry is a belief masquerading
as knowledge, and an orphaned waiver on a non-waived finding is never valid.
AnchorZ.kindwidens to also accept'invariant', unused by any producer yet
(a follow-on phase's scope).The
code-reviewverifier's persisted findings (gates/code-review.ts) now
carry this identity:id,target: 'artifact', and a default
disposition: 'open', alongside their existing §7.1 anchor tag. This required
converging code-review's previously-local 3-severityFinding/FindingSeverity
type onto the shared, persisted 4-severityFindingfrom@manehorizons/cadence-types
(rec-20260727-006's design-doc decision D9 — "oneFindingtype,
discriminated bytarget").CodeReviewFinding/CodeReviewFindingSeverity
remain available frompackages/core/src/contracts/index.tsas backward-compat
aliases of the now-shared type —CodeReviewFindingSeveritycorrespondingly
widens from'high' | 'medium' | 'low'to the full'critical' | 'high' | 'medium' | 'low'union, though no code-review provider constructs'critical'
today.RecommendationSourceZgains a'review'member (rec-20260727-011), so a
future phase that routes code-review findings into the recommendation ledger
can carry real provenance instead of mislabeling themmanual/cadence.All schema changes are purely additive — every pre-phase-236
SUMMARY.json
still parses unchanged. This phase is deliberately schema-and-computation
only: findings-to-ledger auto-routing (creatingRecommendation+Evidence
entries from findings during settle) is not implemented here — that
behavioral work is split to a follow-on phase, recorded inline in
.cadence/ROADMAP.md's Phase 236 entry. -
7ddc72a: Identified code-review findings now route into the recommendation ledger at
settle time (Phase 242,rec-20260731-003) — the behavioral half Phase 236
deliberately deferred. Each finding that carries a stableFinding.id(Phase
236 identity) becomes aRecommendationwithsource: 'review', linked to a
cadence-artifactEvidenceentry whosepathis that settle's
<draftId>-SUMMARY.jsonand whosesummarynames the phase id, draft id, and
SUMMARYcontentHash. Routing is keyed onFinding.id, so a re-settle of an
unchanged phase never mints a duplicate entry for a finding already routed,
and one freshly-mintedscoutIdcovers a whole settle's batch rather than one
per finding. Findings with no stable id (e.g.security-audit, which has no
identity wired in yet) are skipped, never force-routed.RecommendationZgains an optionalsourceFindingId(the dedup key), and
addRecommendationgains optionalsourceand a structuredcadence-artifact
evidence override — both backward compatible; every existing caller keeps
today'ssource: 'manual', free-text-evidence behavior unchanged. Two or more
findings that collide on identity within one settle (rec-20260731-001's
known collision — same file/anchor/severity/normalized-message, no occurrence
discriminant) merge into a singleRecommendationrather than mint one entry
with no trace of the duplicates or N separate entries for one id; per
dec-20260731-001, the identity hash itself is untouched — the merge records
the occurrence count explicitly in the entry's evidence/summary text.A new
recommendations.autoRouteconfig field (boolean, defaulttrue,
alongside the existingautoArchive) gates the step. Like the existing
retro-digest and auto-archive steps, routing is best-effort: a failure (e.g. a
ledger write error) never blocks or fails settle, and always prints a stderr
notice rather than failing silently. This is a settle-time writer only — no
new gate, noGATE_ORDERchange, and no refusal semantics; every existing
gate's pass/refuse verdict is byte-for-byte unchanged. Disposition mutation
(accept / waive / fix / supersede) still has no CLI surface — that stays a
follow-on phase's scope, per Phase 236's own boundary. -
3b95218: Settle can now tell a
mock-verifiedcode-review/security-auditgate
from a real-provider one — closing CADENCE's sole surviving P0 (Phase 232,
rec-20260727-001). PreviouslyCodeReviewResult/SecurityAuditResult
computedprovider/modelin memory but discarded both before persistence,
so a SUMMARY could record only that a review ran, never what ran it.GateProvenanceZgains optionalprovider/modelfields, populated only
for thecode-reviewandsecurity-auditgate entries (every other gate's
entry is unchanged).GateFlagsgains an internalverifierIdentityfield
that gate implementations use to report this identity generically — the
gate registry merges it onto the persisted provenance entry by flag
presence, not by gate name, so no gate-specific special-casing was needed
to express it.This is a SUMMARY shape change, so
SummaryZ.schemaVersionmoves from the
literal1to1 | 2: writers now emit2; readers still accept
pre-existing1records unchanged. A SUMMARY written by a genuinely newer
Cadence (an unrecognized higherschemaVersion) now reports a distinct
"written by a newer Cadence" diagnostic instead of a generic parse/corruption
error, mirroring Phase 223'scontentHash"unverifiable" precedent.No
GATE_ORDERchanges, no gate pass/refuse behavior changes, no new
refusals — this is purely provenance the record was silently dropping. -
0726e40: The kernel / verifier / consumer boundary is now named and lint-enforced.
The split has been ~80% built and unnamed for many phases:
GateImpl/
GATE_REGISTRYtotality plus injected verifier ports already formed a plugin
architecture with no published contract. Phase 234 names it without moving a
package or changing a single gate's behaviour.A new
contracts/module publishes the three roles (kernel,verifier,
consumer) as assertable data — including the governing rule that no plugin
can pass; only the kernel calls green — plus a generic
VerifierPort<I, R>that all seven verifier-backed gates (deep-verify,
code-review,security-audit,plan-review,per-task-verify,
spec-review,ui-spec-review) are expressed at, with no per-gate special
casing. It also re-exports every family's input/result types so callers never
reach intoverify/internals for a type.spec-reviewandui-spec-review— previously the only two verifier-backed
gates with no injection seam — are now resolved through ports, and
specApproveServiceaccepts an optional ports argument for testing. Default
resolution, argument fidelity, and lazy UI-path selection are unchanged, and
are now pinned by tests.An ESLint
no-restricted-importszone fails the build when any module outside
verify/orcontracts/imports one of the seven verifier-family modules
directly instead of the published contract, matching both extensioned and
extensionless specifiers. Statically-imported violations are caught; dynamic
import()is not reachable by this rule and is documented as such.GATE_ORDERand every gate's pass/refuse semantics are unchanged, pinned by a
regression fixture that drives a real ten-gate settle through the production
registry, and the full settled-SUMMARY corpus still parses at both
schemaVersion1 and 2.No runtime dependency added. No package moved. No public CLI or config surface
changed. -
cfe582a: Every settle now derives and reports one whole-run assurance record —
a durable answer to "how strongly was this settle actually verified?"
(Phase 233,rec-20260728-001). Composed from the per-gate verifier identity
persisted in Phase 232 plus the existing per-AC evidence-class ladder
(ai-verified > executed > assertion > mention > unverified), it makes a
settle whose gates all ran undermockvisibly different, in the durable
record, from one verified for real.SummaryZgains an optionalassurancefield:verifierRollup(one entry
per distinct(provider, model)pair observed across gate provenance),
evidenceTally(an exhaustive count over all five evidence classes), and
overall('strong' | 'mixed' | 'weak' | 'unverified', a single
deterministic label). The derivation (deriveAssuranceRecord) is a pure
function of the gate-provenance array and the AC-evidence array only — no
gate-specific special-casing was needed to express it, clearing this phase's
binding tripwire and leaving the door open for further kernel/verifier/
consumer boundary work.assuranceis reported only: it adds no gate, no refusal path, and no
bypass flag, and settle's pass/refuse outcome is byte-for-byte unchanged.
It is covered by Phase 223's settle-time content hash, so a post-settle
hand-edit to it is caught bycadence summary verifyexactly like any other
field, and it is surfaced as an## Assurancesection in both
cadence summary renderand theSUMMARY.mdsidecar. -
bff35bf:
cadence settlenow detects when it is actually executing through a
cadencebinary that resolves OUTSIDE the current repo checkout, despite
that repo having its own local build (rec-20260729-001). This is the exact
bug confirmed on phases 233/234: a stale globally-installedcadencebinary
silently shadowed the checkout's ownpackages/core/bin/cadence.cjs,
producing a downgradedschemaVersion: 1SUMMARY with noassurance
record — and the two binaries reported an identical--versionstring on
the unreleased branch, so version comparison can't catch it.Detection (
detectForeignCadenceBinary,packages/core/src/services/ settle.ts) is a pure, unit-tested function: is the realpath of the binary
actually executing this settle located inside the repo's own toplevel, given
that the repo is recognizably CADENCE's own monorepo (packages/core/bin/ cadence.cjs+.cadence/both present at its root). An ordinary consumer
project settling via a globally-installedcadenceis never a false
positive — that gate is what keeps this narrow.On a mismatch, settle prints a loud stderr banner ("SETTLING VIA A FOREIGN
CADENCE BINARY",buildForeignBinaryBanner— same shape/placement
convention as the existingMOCK_FALLBACK_BANNER) naming both paths and
suggesting the fix, andSummaryZgains an optionalforeignBinaryMismatch
field ({ runningBinaryPath, repoToplevel }) recording the same provenance
on the written SUMMARY so the condition is auditable from the artifact alone.
Likeassurance(phase 233), this is reported only — no gate, no refusal
path, no bypass flag; settle still completes normally either way. The field
is genuinely absent (neverfalse/null) on a matched invocation, which is
the common/correct case.This guard only runs in code that contains it, so it could not have caught
233/234 themselves, and it will not catch a settle run through an
already-publishedcadencebinary that predates this release — it protects
settles going forward, once operators are actually running a build that
includes this fix.
Patch Changes
-
0d6aea6: Fixed
computeFindingId(packages/core/src/verify/finding-identity.ts) minting a
new identity for an unchanged finding in two real cases, either of which caused
Phase 242's ledger dedup (keyed onFinding.id) to miss the finding and route a
duplicateRecommendationfor the same underlying defect. The hash previously
includedanchor.kind,anchor.ref, andseverityalongsidefileand
normalizedmessage— but both anchor and severity can legitimately change
across settles for the same defect: the DRAFT-amendment workflow deliberately
re-anchors a previously-unanchored ("gap") finding once a criterion is added to
cover it (proven bycriteria-anchor-corpus.test.ts's own "AC-5 round trip"
test, which already asserted message/severity/line survive that transition
unchanged but never asserted.iddid — now fixed), andseverityis live LLM
classification under real verifier providers (anthropic/local/host-cli),
so a re-run can legitimately reclassify the same defect's severity. Identity is
now a pure hash over(file, normalized message)only;anchorandseverity
are still accepted ascomputeFindingIdparameters (call-site compatibility)
and remain real, unchanged fields on a stampedFinding— they are simply no
longer identity inputs.computeFindingId's line-number exclusion (unrelated,
pre-existing, phase 236) is untouched.deriveRoutingCandidates(packages/core/src/intelligence/finding-routing.ts)
previously assumed every occurrence of a same-id merge group agreed on
severity"by construction" — true before this fix (severity was a hash
input), false after. It now tracks the most severe occurrence seen across a
merge group and reports that severity/priority on the routed candidate, rather
than silently whichever occurrence happened to be encountered first. -
db225ac: Follow-up to #331 (rec-20260731-002):
createVerifierFactory's three
selection-time credential/prerequisite-missing degrade branches — an explicit
anthropicrequest with noANTHROPIC_API_KEY, an explicitlocalrequest
with noCADENCE_LOCAL_BASE_URL/model, and ahost-clirequest for a family
that hasn't wired a builder — previously emitted only a bare single-line
stderr warning for all 7 verifier seams (specReview,uiSpecReview,
verifier/deep-verify,perTaskVerifier,codeReview,planReview,
securityAudit), unlike the loudMOCK_FALLBACK_BANNERdeep-verify already
gets fromsettle.tswhen its configured provider resolves to mock.All three branches now emit the same loud, multi-line banner (reusing
MOCK_VERIFIER_NOTICE's "not real verification" wording), naming the seam and
the specific missing prerequisite. The silent default-mock fallthrough (no
provider configured, or explicitmock) is untouched, as is
wrapWithFallback's separate per-call runtime warning for a host-cli binary
that fails mid-call (different, higher-frequency event — not in scope here).
settle.ts's own deep-verify pre-check and this new factory-level banner are
disjoint by construction (they branch on mutually exclusive resolved-provider
values), so deep-verify never double-warns. -
Updated dependencies [c27bcb0]
-
Updated dependencies [5cc4085]
-
Updated dependencies [7ddc72a]
-
Updated dependencies [3b95218]
-
Updated dependencies [cfe582a]
-
Updated dependencies [bff35bf]
- @manehorizons/cadence-types@1.53.0
Published Packages
All public packages are published on npm as 1.53.0:
@manehorizons/cadence-core@manehorizons/cadence-host-claude-code@manehorizons/cadence-host-codex@manehorizons/cadence-host-toolkit@manehorizons/cadence-types
Verification
- npm publish completed with provenance in the Release workflow.
- Remote tag
v1.53.0is verified before the GitHub Release is created. - npm package versions and GitHub Release metadata are verified after publish.
- Workflow run: https://github.com/manehorizons/cadence/actions/runs/30720112058