-
v3.5.3 - bounds not stated, costs not counted
StableAll checks were successfultest / node-test (git, 22) (push) Successful in 29stest / node-test (git, 24) (push) Successful in 26stest / node-test (other, 22) (push) Successful in 23stest / node-test (other, 24) (push) Successful in 23stest / node-test (planning, 22) (push) Successful in 1m14stest / node-test (planning, 24) (push) Successful in 1m11stest / node-test (prose, 22) (push) Successful in 29stest / node-test (prose, 24) (push) Successful in 28stest / node-test (review, 22) (push) Successful in 17stest / node-test (review, 24) (push) Successful in 17stest / node-test (routing, 22) (push) Successful in 26stest / node-test (routing, 24) (push) Successful in 28stest / self-verify (push) Successful in 13stest / typecheck (push) Successful in 17sreleased this
2026-08-18 09:34:44 +00:00 | 533 commits to main since this releaseCadence asserted controls it did not hold. The review path stated bounds it
never enforced, the run record claimed to price a run it could not see, and
three controls that already existed and were already correct never reached the
path that needed them. Five phases, thirteen requirements, all of it argued off
this repo's own trace rather than off a guess.Added
-
Turns on the record, and a spend figure that names what it excludes.
trace closenow persists the tool-call count its return already carried, and
trace renderreports turns per dispatch and per role beside a
turns_unrecordedcounter of their own, so a dispatch that reported tokens but
no turns stays distinguishable from one that reported the reverse. The three
surfaces that priced a run from worker-return tokens (/cad-report,
trace suggest,/cad-progress --trace) now name the three sources that
figure leaves out - the orchestrator's own turns, cross-model provider calls,
and figureless returns - instead of presenting it as the run's cost.
SPEND_EXCLUDESis a frozen export, so the list cannot drift into three
versions of itself. -
A dispatch window budgeted off the run record. Six
workflow.max_dispatch_tokens.<role>ceilings, each defaulted to that role's
75th-percentile terminal window on this repo's own record rounded up to the
next 25,000, plusplanning.mjs trace window [--phase <N>]to apply them. The
ceiling is READ after the fact and never enforced at dispatch time, because
nothing can resize or cancel a dispatch already running, and the report says so
rather than implying a bound that does not exist. -
A direction and a target on every retune suggestion.
trace suggestused
to return a bare config key, so/cad-suggestcould print
workflow.max_plan_tasksand no more. Each keyed suggestion now carries the
direction to move it, the value it holds now, and a target where one can be
READ - stepped down the gate ladderroute-table.jsonstates, or taken off the
rung the record shows a role's escalated resolves landing on. A rule that
cannot price a target omits it rather than guessing, and a target that would
name no actual change is omitted too./cad-suggestpresents the tweaks in a
heading of their own with the receipts below, and ends by offering to route the
change to/cad-configrather than by declining to have an apply arm. -
A bulk-output transport, and a register that holds it. Bulk tool output
rides a scratch file at the five sites that prescribed it inline, the rule is
stated once inreferences/conventions.md, and a 17-row register plus
self-verify check 20 refuses an eighteenth inline site.
Fixed
-
The coordinator figure counted hours that were not the coordinator's. The
residue accumulators were keyed on phase number, so one run's last marker
closed at a different run's last event whenever a phase number spanned several
runs. Keyed oncorrinstead, phase 2's reported residue over the live record
falls from 366,716,303 ms to 3,508,747 ms, and the largest single window from
280,613,472 ms to 1,081,370 ms. The name stays: a corr-scoped gap between
worker brackets is time this coordinator held the run. -
The provider response had no ceiling Cadence owned.
request()now
enforces a 4 MiB response limit with its ownover-responsereason, distinct
fromtransport, onreview,consultanddetect-modelsalike; the failure
envelope carries a sanitized 1024-byte excerpt rather than the whole body. A
credential sanitizer sits beside the URL one, coveringBearerechoes,
name=valuepairs in four spellings, quoted multi-word values and camelCase
keys. -
Local validation admitted findings the canonical schema refuses.
FINDING_SCHEMAnow carries the constraintsvalidateFindingsenforces -
minimum,minLength,maxLength,maxItems- and an 18-fixture agreement
table runs both sides and compares verdicts, so the schema and the validator
cannot disagree silently.cadence-core/bin/lib/schema-eval.mjsis a
keyword-limited, zero-dep evaluator. -
The recovery arm named a timeout the dispatch path cannot produce.
execute.mdnow saysturn cap or unusable return, in those words, held
there by a standing prose-agreement check.maxTurns: 200is named where the
default reviewer claims exemption, checked against the rung files' own
frontmatter rather than a literal. -
Three controls that existed, were correct, and never reached their path.
A milestone close now distills its pruned phases into.planning/ARCHIVE.md
before the directories go, so the recall corpus survives the close.
risk-check statusgains anunfiredrow state and refuses a fired range
carrying no receipt for that range, and every blocking fire writes
--plan --base --shaso a receipt names the range it settles rather than
clearing every later one.references/execute-parallel.mdreaches the
sequential branch's detector, fire and status sequence by pointing at it
instead of copying it. -
The plan-task ceiling re-decided, and left where it was.
workflow.max_plan_taskswas argued against both of its forces - cold-prefix
cost and context risk - and lands on 8 unchanged, with the arithmetic written
down indesign-notes/dd-plan-task-ceiling.mdwhere a milestone close cannot
prune it.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
-
v3.5.2 - one reader, one transport
StableAll checks were successfultest / node-test (git, 22) (push) Successful in 26stest / node-test (git, 24) (push) Successful in 23stest / node-test (other, 22) (push) Successful in 20stest / node-test (other, 24) (push) Successful in 19stest / node-test (planning, 22) (push) Successful in 1m14stest / node-test (planning, 24) (push) Successful in 1m13stest / node-test (prose, 22) (push) Successful in 35stest / node-test (prose, 24) (push) Successful in 28stest / node-test (review, 22) (push) Successful in 18stest / node-test (review, 24) (push) Successful in 17stest / node-test (routing, 22) (push) Successful in 27stest / node-test (routing, 24) (push) Successful in 31stest / self-verify (push) Successful in 14stest / typecheck (push) Successful in 17sreleased this
2026-08-16 16:41:44 +00:00 | 655 commits to main since this releaseTwo surfaces where the tree already conceded the correct rule in one place and
prescribed the wrong one somewhere else. Neither is a bug anyone hit yet. Both
came out of an external deep dive againstv3.3.0.Fixed
-
The pre-flight overlap gate could admit a plan pair the commit-time
enforcement would then refuse to separate.plan-overlapandlease-check
each carried their own comparison over declared paths, so they disagreed about
what a directory lease covers. Plan 1 declaringfiles: [src/]beside plan 2
declaringfiles: [src/auth.js]passed the parallel-safety check, and then the
executor's own lease gate refused the commit. Same forsrc/against
src/auth/.Containment now has exactly one definition.
cadence-core/bin/lib/lease-grammar.mjs
exportscovers,intersectsandisRefusedSpelling, and both readers ask it.
A census test inhelper-census.test.mjsgoes red if the comparison is pasted
back anywhere undercadence-core/bin/, test files included, which is the live
failure mode in this tree rather than a hypothetical one.cadence-core/references/plan-frontmatter.mdnow states the trailing-slash
directory-prefix form and says outright thatsrc/authdoes not license
src/authority.js.
Added
-
A file-path transport for every seam flag that carries caller-derived free
text. A capture item, a UAT reply or a milestone label holding$(...)or a
backtick could not ride safely in a double-quoted shell word.planning.mjs
already said so in one place and eleven other sites did it anyway.Five new flags, one reader behind all of them
(cadence-core/bin/lib/text-flag-file.mjs, four refusals: valueless flag,
unreadable path, empty file, both forms given):trace append|close --detail-filetrace append --read-fileuat record --fields-filemilestone-prune --label-filecursor set --next-file
The rule is stated once, in
cadence-core/references/conventions.mdunder
## Caller-derived text, and a committed 36-row register
(cadence-core/bin/lib/text-transport.mjs) records every site with its verdict,
20 caller-derived and 16 out of scope with a reason each.self-verifycheck 19
text-transportreads that register, so a seventeenth inline site is refused
rather than noticed later. 13 prose surfaces acrossworkflows/,references/
andskills/moved onto the transport, and the tag site now uses
git tag -a <version> -F <path>. -
./a.txtandsrc//a.txtare refused with a namedredundant-path-segment
diagnostic at both declaration doors, the frontmatterfiles:list and a
- **Files:**task line. The diagnostic reachesplan-overlap's
frontmatter_issuesand the spelling reaches neither reader. Note that a
refused declaration drops out of the set, solease-check'sdeclaredcount
falls by one for each, with the diagnostic beside it naming why.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
-
v3.5.1 - authorization the repo grants, not the user
StableSome checks failedtest / node-test (git, 22) (push) Successful in 32stest / node-test (git, 24) (push) Successful in 24stest / node-test (other, 22) (push) Failing after 20stest / node-test (other, 24) (push) Failing after 19stest / node-test (planning, 22) (push) Successful in 1m8stest / node-test (planning, 24) (push) Successful in 1m4stest / node-test (prose, 22) (push) Successful in 25stest / node-test (prose, 24) (push) Successful in 24stest / node-test (review, 22) (push) Successful in 15stest / node-test (review, 24) (push) Successful in 19stest / node-test (routing, 22) (push) Successful in 28stest / node-test (routing, 24) (push) Successful in 27stest / self-verify (push) Successful in 13stest / typecheck (push) Successful in 15sreleased this
2026-08-16 02:08:56 +00:00 | 690 commits to main since this releaseFixed
-
A
git.auto_closeset once in your global config authorized an unattended
merge in every repository you own. The key is documented repo-local, and the
close gate enforced it by reading the repository layer, but the GitLab arm of
/cad-landread the merged value instead. Set it globally and any repo with a
GitLab remote would open an MR and merge it with nothing asked. It now resolves
as two separate answers,autoCloseRequestedfrom the merged config and
autoCloseAuthorizedfrom the repository layer alone, and the new
git-publish.mjs authorizedsubcommand is what every host consults before it
touches a remote. Requesting it globally and never authorizing it here now
refuses in wording that says which of the two is missing.The GitLab consult also moved ahead of the reuse probe rather than sitting
beside the create.glab mr createpublishes the source branch itself, and the
reuse arm hands an already-open MR straight to the merge with no create at all,
so a check placed at the create left that path ungated. -
milestone-pruneread only the first physical line of a requirement
bullet, and both halves of the transform were wrong for it. A bullet that
wrapped lost its lead line and left every continuation behind as orphaned
prose, and the archived## Shippedrow got a parenthetical truncated at the
first newline. Three consecutive milestone closes were repaired by hand. It now
reads the whole span, takes both ends of## Activefrom the fence-aware
sectionSpanso a fenced example in a template is not mistaken for the
section, and escapes any|in the summary before it reaches the table cell,
so the row keeps its five columns. -
/cad-landnever once reported the tracker on the repository it was built
in. Host detection compared the origin URL's hostname against thetealogin
list, and a forge whose SSH endpoint is a different name from its web host
matched nothing, which is an ordinary deployment shape rather than a
misconfiguration. The seam now hands the binding toteaitself with
--remote origin, and guards the call rather than the pick: unless some login
NAMES the origin host through its name, API url orssh_host, it declines to
ask and prints theno-loginline it always printed.teadoes not refuse an
unmatched remote, it falls back to config order and answers exit 0, so an
unguarded call would report another server's issues as yours.If your forge has a split endpoint, put the SSH host in the login's
ssh_host
and the report will bind to it.
Changed
- The Forgejo tracker read asks for
--state openinstead of--state all. The
server clamps a page at 50 rows whatever--limitrequests, so on any real
tracker the read was honestly incomplete and the whole report degraded to a
skip line. What that costs is that a referenced number missing from the list is
closed or absent rather than absent, so each unanswered number gets one bounded
tea issues <index>resolve, capped at five per land. A number that neither
the list nor a resolve answered is reported asunresolved, never as closed
and never as not found:teaexits nonzero both for an absent issue and for a
failed read, and this seam discards child stderr, so naming it would be an
affirmative answer about input it could not read.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
-
v3.4.1 - what the config says is what routing does
StableAll checks were successfultest / node-test (git, 22) (push) Successful in 22stest / node-test (git, 24) (push) Successful in 19stest / node-test (other, 22) (push) Successful in 15stest / node-test (other, 24) (push) Successful in 16stest / node-test (planning, 22) (push) Successful in 1m4stest / node-test (planning, 24) (push) Successful in 1m3stest / node-test (prose, 22) (push) Successful in 26stest / node-test (prose, 24) (push) Successful in 30stest / node-test (review, 22) (push) Successful in 19stest / node-test (review, 24) (push) Successful in 17stest / node-test (routing, 22) (push) Successful in 26stest / node-test (routing, 24) (push) Successful in 26stest / self-verify (push) Successful in 13stest / typecheck (push) Successful in 18sreleased this
2026-08-15 19:50:32 +00:00 | 747 commits to main since this releaseFixed
-
The schema said one gate, the route table fired another. Three surfaces
described the review gates and nothing had ever compared them.
config.schema.json:81gavereview.triggers.phase_diff.gatea default of
advisoryand its purpose string said "advisory at shipped", while
route-table.json'sreview.shipped.phase_difffiredoff. That one cell
was the visible half. The invisible half is that aconfig.mjs getof any
gate no layer had set answered with the SCHEMA DEFAULT rather than with what
the stakes level actually fires, so all four triggers could disagree and
nothing said so.All four
review.triggers.*.gatedefaults are now thenullsentinel, and
each purpose string names the gate forsolo,shippedandcriticalread
straight offroute-table.json.risk_surfacemoved with them even though
its three cells agree today: a scalar default that is legal only while every
level's cell equals it passes quietly right up to the first cell that moves.
Thereviewgrid did not move at all - it is the authority, and this release
is the other two surfaces catching up to it.The workaround came out with the defect.
workflows/execute.mdcarried a
paragraph telling a caller not to pre-fetch a gate throughconfig.mjs get
because the answer would be the schema default rather than the level's;
workflows/plan.mdcarried the same one. Both now state the shipped
behaviour, andreferences/config-catalog.md's gate row stops publishing a
per-key scalar default that routing never fires.
Added
-
self-verify.mjscheck 18,gate-agreement. The check that makes the
fix above stay fixed, and the reason one visible cell was worth a release.
self-verify.mjsalready failed in both directions on rung files and on the
three routing grids, and it already readconfig.schema.jsonfor the gate and
stakes vocabularies - it had both files open and had never compared a trigger's
gate across them, so the drift was invisible to the one check whose job is
catching exactly this.It compares every
review.triggers.<t>.gateschema default AND itspurpose
prose againstroute-table.json'sreview[level][trigger], over six codes
(gate-default-drift,gate-default-invalid,gate-prose-missing,
gate-prose-drift,gate-grid-missing,gate-row-malformed). The rule is a
pure lib atcadence-core/bin/lib/gate-agreement.mjs, unit-tested from frozen
fixtures rather than from the live files, so the tests do not move when the
grid does.It was watched to FAIL before the fix landed, not inspected: run against the
unpatched tree it reportedplan,diffandphase_diff, naming
phase_difftogether withshippedby name.
Changed
-
config.mjs getreports an unset gate as unset. A gate no layer pinned
now answersnullwith onewarnings[]entry namingroute.mjs resolveas
what decides it for a level, so a reader can tell "no layer set this, the
stakes level decides" from "this project pinned it". A pinned gate still reads
back byte-identical with no warning, a keylessgetcarries no gate warning at
all, andconfig.mjs check review.triggers.diff.gate=nullstill refuses with
must be one of: off, advisory, blocking, adjudicated- thevaluesarrays
stayed four-membered, sosetandcheckbehave exactly as before.Known gap, filed rather than papered over:
gate-agreementcompares the
default against the cells, not against thenullsentinel, so a gate whose
three cells happen to be identical could regress its default and stay green.
risk_surfaceis that case today.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
-
v3.3.1 - the close stops lying about what it closed
StableAll checks were successfultest / node-test (git, 22) (push) Successful in 22stest / node-test (git, 24) (push) Successful in 25stest / node-test (other, 22) (push) Successful in 18stest / node-test (other, 24) (push) Successful in 18stest / node-test (planning, 22) (push) Successful in 1m27stest / node-test (planning, 24) (push) Successful in 1m19stest / node-test (prose, 22) (push) Successful in 30stest / node-test (prose, 24) (push) Successful in 29stest / node-test (review, 22) (push) Successful in 17stest / node-test (review, 24) (push) Successful in 20stest / node-test (routing, 22) (push) Successful in 31stest / node-test (routing, 24) (push) Successful in 39stest / self-verify (push) Successful in 17stest / typecheck (push) Successful in 22sreleased this
2026-08-15 15:30:21 +00:00 | 782 commits to main since this releaseFirst micro-release of the rolling cadence. Both fixes are
cmdMilestonePrune, so this is one surgery on one function.Fixed
milestone-prunestops reporting a half-finished close as a finished one. (#128)A phase whose directory could not be moved or deleted was collected as a warning and the loop continued, after which ROADMAP.md and REQUIREMENTS.md were pruned for every completed phase anyway and the envelope answered
ok:true, action:"pruned". The comment above that loop promised the opposite, that a rename which throws leaves both documents untouched. It never did, because the throw never left the loop.The directory pass now runs first and only the phases it actually cleared reach the documents, so the tree and the docs still agree. A partial application returns
ok:false, reason:"partial-prune"naming the phases that did not clear, and/cad-milestonehalts on it instead of committing the disagreement. Re-running picks up only what is left.An
_archive-<label>that is a symlink can no longer redirect the archive out of the planning root. (#138)The containment check was lexical, so a pre-existing link resolved inside the tree,
mkdirSyncsucceeded silently against it, andrenameSyncfollowed it. The path is now classified withlstatbefore anything moves, and a per-phase destination left behind by an interrupted close is refused rather than clobbered.Verification
1799 tests pass, self-verify reports no problems, typecheck clean. Four new seam tests, each shown red against the pre-fix seam.
Full changelog: https://git.jcrenshaw.dev/crenshawdev/cadence/compare/v3.3.0...v3.3.1
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
Source code (ZIP)
-
v3.3.0 - the record you plan from
StableSome checks are pendingtest / node-test (git, 22) (push) Waiting to runtest / node-test (git, 24) (push) Waiting to runtest / node-test (other, 22) (push) Waiting to runtest / node-test (other, 24) (push) Waiting to runtest / node-test (planning, 22) (push) Waiting to runtest / node-test (planning, 24) (push) Waiting to runtest / node-test (prose, 22) (push) Waiting to runtest / node-test (prose, 24) (push) Waiting to runtest / node-test (review, 22) (push) Waiting to runtest / node-test (review, 24) (push) Waiting to runtest / node-test (routing, 22) (push) Waiting to runtest / node-test (routing, 24) (push) Waiting to runtest / self-verify (push) Waiting to runtest / typecheck (push) Waiting to runreleased this
2026-08-15 13:59:28 +00:00 | 789 commits to main since this releaseThe evidence Cadence plans and reports from is itself checked this cycle: the
capture queue that silently dropped filed work, the run record that could not
join a provider call to the fire that made it, and the claims the docs make
about the code.Fixed
-
/cad-captureno longer writes where recall cannot see it. Five items
filed after the 2026-08-08 archive block landed below a heading the corpus
walk does not visit and were invisible to/cad-plan's recall until they
were lifted by hand, one of them a[high]finding that a tuning rule can
never fire. The writer lands inside the walk, the tag reader admits every
shape the writer emits,/cad-healthreports any bullet outside the walk,
and a concurrent append can no longer lose an update. -
The run record joins.
corris fire-scoped rather than phase-scoped, so
a provider call attributes to the fire that made it; a terminal event's
--roleis validated against its paired dispatch, so a role with zero
dispatches can no longer render carrying a token total; andrecordedcounts
matched dispatches rather than token-bearing events, so a replayed terminal
stops hiding a missing report. -
String-form
protected_branchesis honored by all four readers, the
fence-blind## Phasesand## Activescanners are guarded, and a blank
--rootis refused consistently rather than linting the cwd and returning
ok:true. -
Fourteen stale claims are corrected at their source.
README.md,
METHOD.mdandcadence-core/workflows/plan.mdstated aplanreview as
advisory atshippedwhereroute-table.jsonresolvesoff;
/cad-new-projectand/cad-adoptreported the config they had just copied
as turning plan check on when the template ships it off;
config-catalog.mdpublished aRiskknob category with zero rows behind
it;recall.mdnamed two callers where the code makes three; and
METHOD.mdandINTERNALS.mdstill described the dispatch-time risk floor
and its per-surface waivers, both retired in v2.7.0.
Added
-
A second
/cad-docs-verifysweep, transcribed and dated.
.planning/DOCS-CLAIMS.mdnow carries 933 rows over 32 files, each with a
generatedruncolumn and a live line cite, 373 line re-pins, and the seven
rows phase 4'strace closeinvalidated rewritten to the live call rather
than silently re-pinned. -
Two prose assertions that derive both sides from the tree, in
cadence-core/bin/prose-agreement.test.mjs: README's skill, role and
rung-file counts measured againstskills/andagents/, and a check that
PROJECT.md's### Activedeclares its milestone before naming any other
version. Both were shown to redden on a pre-fix input. -
planning.mjs criteria-size, so the 3-7 acceptance-criteria ceiling
/cad-contextstates is counted rather than merely written down.
Changed
-
One
trace closesubcommand replaces eight workflow files' restated close
prose,trace renderis bounded by default, and the round-trips measured
as unbatched now issue as one call. -
The
REQ_IDdocumentation states the asymmetry that is live rather than
the head-anchored limitPRS-02removed:REQ_ID_EXACTadmits a
digit-leading category whileREQ_ID_TOKENkeeps its letter head, so an
unbolded2FA-01remains invisible to the prose scan.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
-
v2.7.0: cut the proxies
StableAll checks were successfultest / node-test (git, 22) (push) Successful in 21stest / node-test (git, 24) (push) Successful in 20stest / node-test (other, 22) (push) Successful in 15stest / node-test (other, 24) (push) Successful in 14stest / node-test (planning, 22) (push) Successful in 1m10stest / node-test (planning, 24) (push) Successful in 1m12stest / node-test (prose, 22) (push) Successful in 29stest / node-test (prose, 24) (push) Successful in 29stest / node-test (review, 22) (push) Successful in 22stest / node-test (review, 24) (push) Successful in 21stest / node-test (routing, 22) (push) Successful in 35stest / node-test (routing, 24) (push) Successful in 31stest / self-verify (push) Successful in 20stest / typecheck (push) Successful in 26sreleased this
2026-08-11 18:40:42 +00:00 | 980 commits to main since this releaseEvery mechanism removed in this release fired on something measurable that stood in for the property it actually cared about. Each was defensible alone. Together they are why the tool got slow: every proxy brings its own config key, its own CI check, and its own prose surface.
The dispatch-time risk floor is gone
It judged a file by its name. One path token matched against ~100 common lowercase words raised the whole phase to
critical, which put all six roles on opus atxhighand turnedplan,phase_diffandpre_shipadjudicated at once.Measured on a transcript-recall project:
src/store/session.rsfloored phase 1 onauth;src/store/lock.rsandsrc/ingest/mod.rsfloored phase 2 onconcurrencyanduntrusted_input.ingest/,lockandsessionare that project's native vocabulary, so no phase of it could ever route belowcritical— 15 of 16 resolves ran opus, against a README claim of ~27% routed down to Sonnet.tests/ingest_concurrency.rs, a test file, floored its phase on two surfaces at once. The floor also clampedmodel.effort.<role>, so a configured rung lost to a filename.Cadence already had a better detector for the same question. The commit-time
risk_surfacecheck reads the actual staged diff, and it stays, blocking at every level.workflow.subagent_timeoutis goneThe schema called it "ms before a subagent is killed." No code read it, and the host spawn seam takes no timeout and offers no cancel. The trace shows a 32-minute hang against a configured five-minute bound, unkilled. A missing feature is honest; a knob reporting a control you do not have is a false safety signal.
Plan and phase size are counted, not judged
A phase naming 25 of a project's 46 requirements was planned as 8 tasks against a configured ceiling of 4 — by a planner handed the ceiling and a checker told to flag the overrun. Both passed it.
- New
planning.mjs plan-sizecounts both facts against a ceiling. /cad-plannow sizes the phase before dispatching a planner. Learning a phase is too big cost 10–14 minutes; it now costs a count.- The task ceiling is explicitly per plan. A count ceiling on a large phase does not make tasks smaller, it makes them fatter, which is how one task came to be "scaffolding, license, typed errors and the CLI skeleton."
- The
PHASE TOO BIGprompt gets a third option, and it is the recommended one: split into sequential plans inside the phase. That had been forbidden on a reason that was wrong.
The
shippedreview row is lighterplanadjudicated → advisory: the plan already passedcad-plan-checker, a blocking gate on by default, so this was a second adversarial pass plus a 13-survivor triage menu.diffadvisory → off:execute.mdstates the cost itself — at advisory the fire overlaps the next plan's dispatch and is free, but "the last plan has no next dispatch, so it fires and waits." On a single-plan phase that is 32 minutes of serial tail for findings that gate nothing.risk_surfacestill halts per risky commit andpre_shipstill adjudicates the whole branch at land.criticalis untouched.docs/EVIDENCE.mdkeeps the definitions, drops the numbersAbout 200 measured figures across six asserted tables were derived data carried in the tree, with tests proving the copy still matched a source that recomputes in 200ms. One byte changed in any of 99 surfaces could stale five tables at once.
Falsified after the cut — appending one sentence to
references/seams.md:signals before 6, across 4 tables it never touched after 1, naming the file that grew No check weakened. The byte ceiling and
unbudgeted-surfaceboth stand, andweight.mjsstill measures on demand.Also
- The weight budget is a ceiling, not an equality.
budget-undershootfired on any shrink, taxing a prose cut at the rate it taxed growth. - Prose no longer restates measured byte figures. Eleven deferral sites carried a hardcoded size a test then verified against the tree. Cite the file, not its size.
- The test suite runs by group:
node cadence-core/bin/test.mjs routingis ~2.5s against ~11.5s for the tree, and CI runs one job per group. review-provider.test.mjswas writing into Cadence's own.planning/trace.jsonl— 1,443 fixture rows against 33 real ones.
Breaking
Nine config keys retired, all through
lib/retired-keys.mjs, so an existing config warns and routes normally rather than breaking.config.mjs setrefuses them.risk.override.{auth,migrations,billing,concurrency,destructive,secrets,api_contract,untrusted_input}workflow.subagent_timeout
Verification
node cadence-core/bin/test.mjs— 1,369 pass, 0 fail ·self-verify— 0 problems ·tsc -p tsconfig.ci.json— clean · 8 commits, 53 files, +977 / −3,021Downloads
-
Source code (ZIP)
3 downloads
-
Source code (TAR.GZ)
2 downloads
- New
-
released this
2026-08-09 19:54:07 +00:00 | 1033 commits to main since this releaseThe four defects
2.6.0's own doc sweep found and filed rather than reworded
away, plus what the pre-ship review caught on the way out. One phase and a
review pass, 14 commits. Every fix here ships with a check that was watched to
fail against the unpatched code first, because a check nobody has seen fail is
a guess wearing a verdict.The review pass is why this is 14 commits and not six. A cross-model reviewer
over the branch diff returned eight findings and every one of them held up
against the tree, including a blocking review gate that could not fire at all
and a harvester reporting itself as technical debt. Two of them were defects in
the fixes for the other six, caught by re-firing the same gate against the tree
that actually ships.Fixed
-
cadence-core/bin/lib/trace.mjsno longer contains two literal NUL bytes.
They were typed into the worker key's template string instead of the\0
escape, sofile(1)called the sourcedataand everygreporrgover
cadence-core/bin/**silently skipped that whole file without-a. It cost me
a debugging detour during 2.6.0's UAT and it would have cost the next one too.
The separator is still U+0000 at runtime, only the source bytes changed.
self-verifygained check 15,nul-byte-in-source, over every regular file
undercadence-core/binincluding the test files, so it cannot come back. -
The
phase_diffgate row saidoff / off / adjudicatedwhile the resolver
returnedoff / advisory / adjudicated. Wrong in
cadence-core/references/review-triggers.mdand indocs/WORKFLOW.md's copy
of it, and four of 2.6.0's eighteen stale doc rows were that one row copied.
A newprose-agreement.test.mjsdrivesroute.mjs resolveat all three stakes
levels and compares both prose sites to what actually comes back, so the copy
cannot drift from the code again. -
The plan checker's contract contradicted itself about its own size.
:42
said "Check six dimensions" and listed six, while:113's success criterion
still said "All five dimensions checked." A checker whose completion
criterion names five of six can report success having skipped the
dimension 2.5.0 shipped a cycle early to bound plan size. The count is now
pinned three ways: the number in each block, and the number of dimensions
actually enumerated between them. -
The
risk_surfacewiring row admitted shape (c) only as "the flagged-diff
FILE path the checkpoint returned."/cad-taskis the one fire site with no
executor and no checkpoint, so it produced a shape-(c) path the row did not
describe. The qualifier is gone;cadence-core/workflows/task.mdkeeps its
named transient path, its never-stage rule and its delete-on-return cleanup. -
/cad-task'srisk_surfacefire could not run at all on the inline path.
The step told the model to write the flagged diff to
.planning/tasks/{slug}/risk-task-{slug}.diff, but{slug}and that
directory are created only by the PLANNED path, which itself declines to
create them when.planning/is absent. On an inline task, or in any repo
with no.planning/, the redirect failedNo such file or directoryand the
trigger isblockingat every level, so this was a blocking gate that could
not fire rather than one that passed. The planned path keeps the named path
it already owns; inline writes to${TMPDIR:-/tmp}and leaves no directory
behind. -
trace append --tokens 146,405no longer throws the whole append away.
A malformed--tokensappends nothing by design, so that the caller cannot
believe a figure was recorded when it was dropped. But this plugin prints
token figures comma-grouped three lines above the order that copies them, so
the grouped form is the transcription its own prose models. Refusing it left
thedispatchhalf of the bracket open and the worker strandedunpaired
forever, which escalates a recording error into loss of the bracket it was
recording. Grouping is stripped only in the strict 3-digit shape, so1,2,3
and146,40are still refused. -
The technical-debt harvester stopped reporting itself as technical debt.
markerSegments' doc comment spelled the marker token followed by a colon,
sodebt-harvestfound exactly one marker in the whole tree and it was the
file doing the finding: a corner-cut that does not exist, landing in the
queue a human triages. Both the note aboveDEBT_TOKENand
conventions.mdalready stated that documentation never writes a literal
marker line. -
The four
docs/EVIDENCE.mdtables nothing was checking are now pinned.
The byte checks above covered the twelve-largest table and the per-directory
subtotals. The turn-one table, eager-vs-reachable, zero-resident and dispatch
are measured by a different seam and were asserted against nothing, so adding
a sentence to any workflow and re-pinning its budget left/cad-execute's
turn-one and reachable figures both silently wrong. The file also stops
naming a provenance commit, which is the same staleness one level up. -
The planning docs agree on which milestone is open. The
v2.6.1close
ran the manifest bump and the phase prune and stopped, leavingDFC-01..04
pointing at a phase the prune had removed:/cad-auditreturned 4/4 broken,
a hard FAIL, so the next milestone would have halted at its own gate before
it could tag.PROJECT.mdseparately still calledv2.5.0the current
release, two releases behind the manifest, andCONTRIBUTING.mdstill
described the byte-budget check as failing only on growth.
Changed
- A budgeted surface now fails
self-verifywhen it SHRINKS, not just when it
grows.docs/EVIDENCE.mdpublished "93 surfaces at exactly their byte count,
total slack 0" as if that were enforced. It was a maintenance convention: the
check readbytes > budget, so any deletion sailed through and the entry went
stale in silence. That is howreview-triggers.mdcame to be quoted at
17,733 B in four places at once. The newbudget-undershootkind names the
direction, and the four sites quoting that file's size moved together to 17,714
with aweighAll-backed test over every row of the twelve-largest table.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
2 downloads
-
-
released this
2026-08-09 19:54:07 +00:00 | 1058 commits to main since this releaseThe reconciliation cycle.
2.5.0closed early and handed this release its
deferred half, so nothing here is new construction: it is the queue nobody could
read, the friction you hit by hand every session, the defects Cadence's own
seams hit on other people's projects, and the first honest measurement of what a
phase costs. 73 commits across five phases.Three of the five phases were scoped by running Cadence against real projects
rather than by reading its own source. A parser pass planned from reading
planning-files.mjswas cut when running that parser over every plan file in
five live projects produced zero issues, and the two things the same survey found
actually broken took its place.Added
-
Per-role token accounting.
trace appendtakes--tokens,--roleand
--readon lifecycle events, andplanning.mjs trace renderand
/cad-progress --traceprint what each role cost and how many dispatches it
took. A role that ran without a token figure reports anunrecordeddispatch
count beside its total rather than a zero, because those are different claims.
All five phase-scoped dispatch sites now bracket their workers, held in place
by a per-file census in the test suite. Before this, 71% of subagent spend
happened at sites nothing was measuring: 206,901 tokens for the assumptions
analyzer, 346,882 for planner plus checker plus revision, 219,068 for two
reviewers, against 310,503 for the only two sites that were bracketed. -
A runaway-loop bound on every dispatched agent. All 19 rung files carry
maxTurns: 400. A spike ran first to establish what the host actually returns
at the cap, because shipping a value that converts a long executor run into a
failed dispatch would have been worse than no bound at all. -
CADENCE-DEBTmarkers and a harvest seam. A deliberate corner-cut carries
a marker at its location naming its ceiling and the trigger that should prompt
revisiting it, andplanning.mjs debt-harvestcollects them into
.planning/CAPTURE.md. Idempotent, with its own regression test, because the
queue is a regenerable view and the marker in tracked code is the record. -
A committed doc-claim ledger.
.planning/DOCS-CLAIMS.mdholds every claim
a/cad-docs-verifysweep raised with its verdict and its resolution, so the
next cycle re-verifies a fixed set of ids instead of re-extracting from scratch
and calling the difference progress. -
docs/EVIDENCE.md. Turn-one bytes for all 23 commands, eager against
reachable for the ten heaviest, dispatch bytes for all 19 rung agents, each
table printed beside the exactweight.mjscommand that regenerates it.
Changed
-
The verify walk runs what it can before it asks you anything.
/cad-verify
now states the bar out loud: an item is a human check only when the model
cannot execute it, meaning irreversible against real data, or outside its reach
(credentials, a GUI, hardware, another machine). Everything else is executed
and cited as a results table. A walk of nine read-only commands and one
destructive one ends the turn asking about one item, not ten. Model-executed
results carry their own provenance, distinct from a user's answer and from a
verifier's. -
The capture queue stops being append-only. 213 open items became 28
current-cycle items each carrying a dated, tree-backed verdict, with the 185
historical ones moved under a single dated## Archiveblock that
planning.mjs recallcannot see. That file is the input to every planning
dispatch's recall, so its noise was being paid for on every one. -
Phase directories are numeric-only, and Cadence says so.
08-meteogram-legend
is not a phase directory,/cad-healthreports one as a violation, and a
numeric-prefix collision produces a named diagnostic instead of one directory
silently shadowing the other. This is a breaking change for a project using
named directories, taken deliberately: the seams were already unusable there,
and an honest refusal beats a clean wrong answer.
Fixed
-
A blocking review can no longer re-arm without bound on its own fix. The
cap is one round, written once in the consequence gate every fire site shares,
so it reaches/cad-execute,/cad-task,/cad-debug,/cad-verifyand the
git guard rather than one of them. Exceeding it hands the remainder to you with
a named reason. -
A planning-doc version the project already published is caught at the ship
gate./cad-auditemits a verdict-movingversion_driftbreak, compared
against the repo's own git tags rather than the plugin manifest, which resolves
relative to the script and would have compared your milestone against Cadence's
version in any project that is not this one. That is issue #87's failure mode,
which happened on this repo in July. -
A project Cadence creates keeps its run record out of git.
execute.mdasserted.planning/trace.jsonl"is gitignored" as the reason a
worktree's trace cannot ride a merge back, and nothing in Cadence wrote that
line. It held here only because it was added by hand. Every other Cadence
project was committing its routing, provider and worker events on the next
git add .planning. -
--phasecarries the string you typed, at every seam that takes it.
--phase 1.10was readingphases/1.1and answering about a different phase. -
REQ_ID_EXACTadmits2FA-01. A requirement id whose category does not
start with a letter was refused, a regression from1.4.0. -
18 stale claims across
README.md,METHOD.md,INTERNALS.md,
CONTRIBUTING.mdand six workflow files. A full sweep of the doc surface
checked 547 claims against the live code: 509 accurate, 18 stale, 20 that
cannot be settled mechanically. Every stale one is corrected against its own
evidence or recorded as a divergence with the reason it stands. Four turned out
to describe real defects rather than stale prose and are filed as their own
requirements instead of being reworded away, including a wiring-table row that
was the single source of four separate wrong claims, and two literal NUL bytes
inlib/trace.mjsthat makegrepskip that file without-a.
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
-
v2.5.0
Stablereleased this
2026-08-08 21:28:08 +00:00 | 1132 commits to main since this releaseDownloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
2 downloads
-
Source code (ZIP)