• v3.5.3 f4bec975d1

    v3.5.3 - bounds not stated, costs not counted
    All checks were successful
    test / node-test (git, 22) (push) Successful in 29s
    test / node-test (git, 24) (push) Successful in 26s
    test / node-test (other, 22) (push) Successful in 23s
    test / node-test (other, 24) (push) Successful in 23s
    test / node-test (planning, 22) (push) Successful in 1m14s
    test / node-test (planning, 24) (push) Successful in 1m11s
    test / node-test (prose, 22) (push) Successful in 29s
    test / node-test (prose, 24) (push) Successful in 28s
    test / node-test (review, 22) (push) Successful in 17s
    test / node-test (review, 24) (push) Successful in 17s
    test / node-test (routing, 22) (push) Successful in 26s
    test / node-test (routing, 24) (push) Successful in 28s
    test / self-verify (push) Successful in 13s
    test / typecheck (push) Successful in 17s
    Stable

    john released this 2026-08-18 09:34:44 +00:00 | 533 commits to main since this release

    Signed by john
    GPG key ID: 693AB15F91734B0C

    Cadence 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 close now persists the tool-call count its return already carried, and
      trace render reports turns per dispatch and per role beside a
      turns_unrecorded counter 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_EXCLUDES is 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, plus planning.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 suggest used
      to return a bare config key, so /cad-suggest could print
      workflow.max_plan_tasks and 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 ladder route-table.json states, 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-suggest presents the tweaks in a
      heading of their own with the receipts below, and ends by offering to route the
      change to /cad-config rather 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 in references/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 on corr instead, 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 own over-response reason, distinct
      from transport, on review, consult and detect-models alike; the failure
      envelope carries a sanitized 1024-byte excerpt rather than the whole body. A
      credential sanitizer sits beside the URL one, covering Bearer echoes,
      name=value pairs in four spellings, quoted multi-word values and camelCase
      keys.

    • Local validation admitted findings the canonical schema refuses.
      FINDING_SCHEMA now carries the constraints validateFindings enforces -
      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.mjs is a
      keyword-limited, zero-dep evaluator.

    • The recovery arm named a timeout the dispatch path cannot produce.
      execute.md now says turn cap or unusable return, in those words, held
      there by a standing prose-agreement check. maxTurns: 200 is 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 status gains an unfired row state and refuses a fired range
      carrying no receipt for that range, and every blocking fire writes
      --plan --base --sha so a receipt names the range it settles rather than
      clearing every later one. references/execute-parallel.md reaches 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_tasks was argued against both of its forces - cold-prefix
      cost and context risk - and lands on 8 unchanged, with the arithmetic written
      down in design-notes/dd-plan-task-ceiling.md where a milestone close cannot
      prune it.

    Downloads
  • v3.5.2 babde6638a

    v3.5.2 - one reader, one transport
    All checks were successful
    test / node-test (git, 22) (push) Successful in 26s
    test / node-test (git, 24) (push) Successful in 23s
    test / node-test (other, 22) (push) Successful in 20s
    test / node-test (other, 24) (push) Successful in 19s
    test / node-test (planning, 22) (push) Successful in 1m14s
    test / node-test (planning, 24) (push) Successful in 1m13s
    test / node-test (prose, 22) (push) Successful in 35s
    test / node-test (prose, 24) (push) Successful in 28s
    test / node-test (review, 22) (push) Successful in 18s
    test / node-test (review, 24) (push) Successful in 17s
    test / node-test (routing, 22) (push) Successful in 27s
    test / node-test (routing, 24) (push) Successful in 31s
    test / self-verify (push) Successful in 14s
    test / typecheck (push) Successful in 17s
    Stable

    john released this 2026-08-16 16:41:44 +00:00 | 655 commits to main since this release

    Signed by john
    GPG key ID: 693AB15F91734B0C

    Two 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 against v3.3.0.

    Fixed

    • The pre-flight overlap gate could admit a plan pair the commit-time
      enforcement would then refuse to separate.
      plan-overlap and lease-check
      each carried their own comparison over declared paths, so they disagreed about
      what a directory lease covers. Plan 1 declaring files: [src/] beside plan 2
      declaring files: [src/auth.js] passed the parallel-safety check, and then the
      executor's own lease gate refused the commit. Same for src/ against
      src/auth/.

      Containment now has exactly one definition. cadence-core/bin/lib/lease-grammar.mjs
      exports covers, intersects and isRefusedSpelling, and both readers ask it.
      A census test in helper-census.test.mjs goes red if the comparison is pasted
      back anywhere under cadence-core/bin/, test files included, which is the live
      failure mode in this tree rather than a hypothetical one.

      cadence-core/references/plan-frontmatter.md now states the trailing-slash
      directory-prefix form and says outright that src/auth does 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-file
      • trace append --read-file
      • uat record --fields-file
      • milestone-prune --label-file
      • cursor set --next-file

      The rule is stated once, in cadence-core/references/conventions.md under
      ## 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-verify check 19
      text-transport reads that register, so a seventeenth inline site is refused
      rather than noticed later. 13 prose surfaces across workflows/, references/
      and skills/ moved onto the transport, and the tag site now uses
      git tag -a <version> -F <path>.

    • ./a.txt and src//a.txt are refused with a named redundant-path-segment
      diagnostic at both declaration doors
      , the frontmatter files: list and a
      - **Files:** task line. The diagnostic reaches plan-overlap's
      frontmatter_issues and the spelling reaches neither reader. Note that a
      refused declaration drops out of the set, so lease-check's declared count
      falls by one for each, with the diagnostic beside it naming why.

    Downloads
  • v3.5.1 8b2b81e2f1

    v3.5.1 - authorization the repo grants, not the user
    Some checks failed
    test / node-test (git, 22) (push) Successful in 32s
    test / node-test (git, 24) (push) Successful in 24s
    test / node-test (other, 22) (push) Failing after 20s
    test / node-test (other, 24) (push) Failing after 19s
    test / node-test (planning, 22) (push) Successful in 1m8s
    test / node-test (planning, 24) (push) Successful in 1m4s
    test / node-test (prose, 22) (push) Successful in 25s
    test / node-test (prose, 24) (push) Successful in 24s
    test / node-test (review, 22) (push) Successful in 15s
    test / node-test (review, 24) (push) Successful in 19s
    test / node-test (routing, 22) (push) Successful in 28s
    test / node-test (routing, 24) (push) Successful in 27s
    test / self-verify (push) Successful in 13s
    test / typecheck (push) Successful in 15s
    Stable

    john released this 2026-08-16 02:08:56 +00:00 | 690 commits to main since this release

    Signed by john
    GPG key ID: 693AB15F91734B0C

    Fixed

    • A git.auto_close set 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-land read 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, autoCloseRequested from the merged config and
      autoCloseAuthorized from the repository layer alone, and the new
      git-publish.mjs authorized subcommand 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 create publishes 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-prune read 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 ## Shipped row 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 ## Active from the fence-aware
      sectionSpan so 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-land never once reported the tracker on the repository it was built
      in.
      Host detection compared the origin URL's hostname against the tea login
      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 to tea itself with
      --remote origin, and guards the call rather than the pick: unless some login
      NAMES the origin host through its name, API url or ssh_host, it declines to
      ask and prints the no-login line it always printed. tea does 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 open instead of --state all. The
      server clamps a page at 50 rows whatever --limit requests, 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 as unresolved, never as closed
      and never as not found: tea exits 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
  • v3.4.1 bb8cd5a4e8

    v3.4.1 - what the config says is what routing does
    All checks were successful
    test / node-test (git, 22) (push) Successful in 22s
    test / node-test (git, 24) (push) Successful in 19s
    test / node-test (other, 22) (push) Successful in 15s
    test / node-test (other, 24) (push) Successful in 16s
    test / node-test (planning, 22) (push) Successful in 1m4s
    test / node-test (planning, 24) (push) Successful in 1m3s
    test / node-test (prose, 22) (push) Successful in 26s
    test / node-test (prose, 24) (push) Successful in 30s
    test / node-test (review, 22) (push) Successful in 19s
    test / node-test (review, 24) (push) Successful in 17s
    test / node-test (routing, 22) (push) Successful in 26s
    test / node-test (routing, 24) (push) Successful in 26s
    test / self-verify (push) Successful in 13s
    test / typecheck (push) Successful in 18s
    Stable

    john released this 2026-08-15 19:50:32 +00:00 | 747 commits to main since this release

    Signed by john
    GPG key ID: 693AB15F91734B0C

    Fixed

    • 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:81 gave review.triggers.phase_diff.gate a default of
      advisory and its purpose string said "advisory at shipped", while
      route-table.json's review.shipped.phase_diff fired off. That one cell
      was the visible half. The invisible half is that a config.mjs get of 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.*.gate defaults are now the null sentinel, and
      each purpose string names the gate for solo, shipped and critical read
      straight off route-table.json. risk_surface moved 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.
      The review grid 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.md carried a
      paragraph telling a caller not to pre-fetch a gate through config.mjs get
      because the answer would be the schema default rather than the level's;
      workflows/plan.md carried the same one. Both now state the shipped
      behaviour, and references/config-catalog.md's gate row stops publishing a
      per-key scalar default that routing never fires.

    Added

    • self-verify.mjs check 18, gate-agreement. The check that makes the
      fix above stay fixed, and the reason one visible cell was worth a release.
      self-verify.mjs already failed in both directions on rung files and on the
      three routing grids, and it already read config.schema.json for 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>.gate schema default AND its purpose
      prose against route-table.json's review[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 at cadence-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 reported plan, diff and phase_diff, naming
      phase_diff together with shipped by name.

    Changed

    • config.mjs get reports an unset gate as unset. A gate no layer pinned
      now answers null with one warnings[] entry naming route.mjs resolve as
      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 keyless get carries no gate warning at
      all, and config.mjs check review.triggers.diff.gate=null still refuses with
      must be one of: off, advisory, blocking, adjudicated - the values arrays
      stayed four-membered, so set and check behave exactly as before.

      Known gap, filed rather than papered over: gate-agreement compares the
      default against the cells, not against the null sentinel, so a gate whose
      three cells happen to be identical could regress its default and stay green.
      risk_surface is that case today.

    Downloads
  • v3.3.1 7693777b55

    v3.3.1 - the close stops lying about what it closed
    All checks were successful
    test / node-test (git, 22) (push) Successful in 22s
    test / node-test (git, 24) (push) Successful in 25s
    test / node-test (other, 22) (push) Successful in 18s
    test / node-test (other, 24) (push) Successful in 18s
    test / node-test (planning, 22) (push) Successful in 1m27s
    test / node-test (planning, 24) (push) Successful in 1m19s
    test / node-test (prose, 22) (push) Successful in 30s
    test / node-test (prose, 24) (push) Successful in 29s
    test / node-test (review, 22) (push) Successful in 17s
    test / node-test (review, 24) (push) Successful in 20s
    test / node-test (routing, 22) (push) Successful in 31s
    test / node-test (routing, 24) (push) Successful in 39s
    test / self-verify (push) Successful in 17s
    test / typecheck (push) Successful in 22s
    Stable

    john released this 2026-08-15 15:30:21 +00:00 | 782 commits to main since this release

    Signed by john
    GPG key ID: 693AB15F91734B0C

    First micro-release of the rolling cadence. Both fixes are cmdMilestonePrune, so this is one surgery on one function.

    Fixed

    milestone-prune stops 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-milestone halts 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, mkdirSync succeeded silently against it, and renameSync followed it. The path is now classified with lstat before 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
  • v3.3.0 360fb226f4

    v3.3.0 - the record you plan from
    Some checks are pending
    test / node-test (git, 22) (push) Waiting to run
    test / node-test (git, 24) (push) Waiting to run
    test / node-test (other, 22) (push) Waiting to run
    test / node-test (other, 24) (push) Waiting to run
    test / node-test (planning, 22) (push) Waiting to run
    test / node-test (planning, 24) (push) Waiting to run
    test / node-test (prose, 22) (push) Waiting to run
    test / node-test (prose, 24) (push) Waiting to run
    test / node-test (review, 22) (push) Waiting to run
    test / node-test (review, 24) (push) Waiting to run
    test / node-test (routing, 22) (push) Waiting to run
    test / node-test (routing, 24) (push) Waiting to run
    test / self-verify (push) Waiting to run
    test / typecheck (push) Waiting to run
    Stable

    john released this 2026-08-15 13:59:28 +00:00 | 789 commits to main since this release

    Signed by john
    GPG key ID: 693AB15F91734B0C

    The 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-capture no 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-health reports any bullet outside the walk,
      and a concurrent append can no longer lose an update.

    • The run record joins. corr is fire-scoped rather than phase-scoped, so
      a provider call attributes to the fire that made it; a terminal event's
      --role is validated against its paired dispatch, so a role with zero
      dispatches can no longer render carrying a token total; and recorded counts
      matched dispatches rather than token-bearing events, so a replayed terminal
      stops hiding a missing report.

    • String-form protected_branches is honored by all four readers, the
      fence-blind ## Phases and ## Active scanners are guarded, and a blank
      --root is refused consistently rather than linting the cwd and returning
      ok:true.

    • Fourteen stale claims are corrected at their source. README.md,
      METHOD.md and cadence-core/workflows/plan.md stated a plan review as
      advisory at shipped where route-table.json resolves off;
      /cad-new-project and /cad-adopt reported the config they had just copied
      as turning plan check on when the template ships it off;
      config-catalog.md published a Risk knob category with zero rows behind
      it; recall.md named two callers where the code makes three; and
      METHOD.md and INTERNALS.md still described the dispatch-time risk floor
      and its per-surface waivers, both retired in v2.7.0.

    Added

    • A second /cad-docs-verify sweep, transcribed and dated.
      .planning/DOCS-CLAIMS.md now carries 933 rows over 32 files, each with a
      generated run column and a live line cite, 373 line re-pins, and the seven
      rows phase 4's trace close invalidated 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 against skills/ and agents/, and a check that
      PROJECT.md's ### Active declares 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-context states is counted rather than merely written down.

    Changed

    • One trace close subcommand replaces eight workflow files' restated close
      prose
      , trace render is bounded by default, and the round-trips measured
      as unbatched now issue as one call.

    • The REQ_ID documentation states the asymmetry that is live rather than
      the head-anchored limit PRS-02 removed: REQ_ID_EXACT admits a
      digit-leading category while REQ_ID_TOKEN keeps its letter head, so an
      unbolded 2FA-01 remains invisible to the prose scan.

    Downloads
  • v2.7.0 ddcbd84fc8

    v2.7.0: cut the proxies
    All checks were successful
    test / node-test (git, 22) (push) Successful in 21s
    test / node-test (git, 24) (push) Successful in 20s
    test / node-test (other, 22) (push) Successful in 15s
    test / node-test (other, 24) (push) Successful in 14s
    test / node-test (planning, 22) (push) Successful in 1m10s
    test / node-test (planning, 24) (push) Successful in 1m12s
    test / node-test (prose, 22) (push) Successful in 29s
    test / node-test (prose, 24) (push) Successful in 29s
    test / node-test (review, 22) (push) Successful in 22s
    test / node-test (review, 24) (push) Successful in 21s
    test / node-test (routing, 22) (push) Successful in 35s
    test / node-test (routing, 24) (push) Successful in 31s
    test / self-verify (push) Successful in 20s
    test / typecheck (push) Successful in 26s
    Stable

    john released this 2026-08-11 18:40:42 +00:00 | 980 commits to main since this release

    Signed by john
    GPG key ID: 693AB15F91734B0C

    Every 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 at xhigh and turned plan, phase_diff and pre_ship adjudicated at once.

    Measured on a transcript-recall project: src/store/session.rs floored phase 1 on auth; src/store/lock.rs and src/ingest/mod.rs floored phase 2 on concurrency and untrusted_input. ingest/, lock and session are that project's native vocabulary, so no phase of it could ever route below critical — 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 clamped model.effort.<role>, so a configured rung lost to a filename.

    Cadence already had a better detector for the same question. The commit-time risk_surface check reads the actual staged diff, and it stays, blocking at every level.

    workflow.subagent_timeout is gone

    The 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-size counts both facts against a ceiling.
    • /cad-plan now 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 BIG prompt 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 shipped review row is lighter

    plan adjudicated → advisory: the plan already passed cad-plan-checker, a blocking gate on by default, so this was a second adversarial pass plus a 13-survivor triage menu.

    diff advisory → off: execute.md states 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_surface still halts per risky commit and pre_ship still adjudicates the whole branch at land. critical is untouched.

    docs/EVIDENCE.md keeps the definitions, drops the numbers

    About 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-surface both stand, and weight.mjs still measures on demand.

    Also

    • The weight budget is a ceiling, not an equality. budget-undershoot fired 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 routing is ~2.5s against ~11.5s for the tree, and CI runs one job per group.
    • review-provider.test.mjs was 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 set refuses 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,021

    Downloads
  • v2.6.1 23cfd1a3c7

    v2.6.1 — the defects the sweep found
    All checks were successful
    test / node-test (22) (push) Successful in 1m10s
    test / node-test (24) (push) Successful in 1m14s
    test / self-verify (push) Successful in 16s
    test / typecheck (push) Successful in 20s
    Stable

    john released this 2026-08-09 19:54:07 +00:00 | 1033 commits to main since this release

    The 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.mjs no longer contains two literal NUL bytes.
      They were typed into the worker key's template string instead of the \0
      escape, so file(1) called the source data and every grep or rg over
      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-verify gained check 15, nul-byte-in-source, over every regular file
      under cadence-core/bin including the test files, so it cannot come back.

    • The phase_diff gate row said off / off / adjudicated while the resolver
      returned off / advisory / adjudicated.
      Wrong in
      cadence-core/references/review-triggers.md and in docs/WORKFLOW.md's copy
      of it, and four of 2.6.0's eighteen stale doc rows were that one row copied.
      A new prose-agreement.test.mjs drives route.mjs resolve at 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_surface wiring row admitted shape (c) only as "the flagged-diff
      FILE path the checkpoint returned."
      /cad-task is 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.md keeps its
      named transient path, its never-stage rule and its delete-on-return cleanup.

    • /cad-task's risk_surface fire 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 failed No such file or directory and the
      trigger is blocking at 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,405 no longer throws the whole append away.
      A malformed --tokens appends 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
      the dispatch half of the bracket open and the worker stranded unpaired
      forever, which escalates a recording error into loss of the bracket it was
      recording. Grouping is stripped only in the strict 3-digit shape, so 1,2,3
      and 146,40 are still refused.

    • The technical-debt harvester stopped reporting itself as technical debt.
      markerSegments' doc comment spelled the marker token followed by a colon,
      so debt-harvest found 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 above DEBT_TOKEN and
      conventions.md already stated that documentation never writes a literal
      marker line.

    • The four docs/EVIDENCE.md tables 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.1 close
      ran the manifest bump and the phase prune and stopped, leaving DFC-01..04
      pointing at a phase the prune had removed: /cad-audit returned 4/4 broken,
      a hard FAIL, so the next milestone would have halted at its own gate before
      it could tag. PROJECT.md separately still called v2.5.0 the current
      release, two releases behind the manifest, and CONTRIBUTING.md still
      described the byte-budget check as failing only on growth.

    Changed

    • A budgeted surface now fails self-verify when it SHRINKS, not just when it
      grows.
      docs/EVIDENCE.md published "93 surfaces at exactly their byte count,
      total slack 0" as if that were enforced. It was a maintenance convention: the
      check read bytes > budget, so any deletion sailed through and the entry went
      stale in silence. That is how review-triggers.md came to be quoted at
      17,733 B in four places at once. The new budget-undershoot kind names the
      direction, and the four sites quoting that file's size moved together to 17,714
      with a weighAll-backed test over every row of the twelve-largest table.
    Downloads
  • v2.6.0 0e23d8e608

    john released this 2026-08-09 19:54:07 +00:00 | 1058 commits to main since this release

    The reconciliation cycle. 2.5.0 closed 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.mjs was 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 append takes --tokens, --role and
      --read on lifecycle events, and planning.mjs trace render and
      /cad-progress --trace print what each role cost and how many dispatches it
      took. A role that ran without a token figure reports an unrecorded dispatch
      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-DEBT markers 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, and planning.mjs debt-harvest collects 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.md holds every claim
      a /cad-docs-verify sweep 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 exact weight.mjs command 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 ## Archive block that
      planning.mjs recall cannot 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-health reports 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-verify and 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-audit emits a verdict-moving version_drift break, 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.md asserted .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.

    • --phase carries the string you typed, at every seam that takes it.
      --phase 1.10 was reading phases/1.1 and answering about a different phase.

    • REQ_ID_EXACT admits 2FA-01. A requirement id whose category does not
      start with a letter was refused, a regression from 1.4.0.

    • 18 stale claims across README.md, METHOD.md, INTERNALS.md,
      CONTRIBUTING.md and 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
      in lib/trace.mjs that make grep skip that file without -a.

    Downloads
  • v2.5.0 feb43ef61c

    v2.5.0
    All checks were successful
    test / node-test (22) (push) Successful in 1m6s
    test / node-test (24) (push) Successful in 1m0s
    test / self-verify (push) Successful in 13s
    test / typecheck (push) Successful in 17s
    Stable

    john released this 2026-08-08 21:28:08 +00:00 | 1132 commits to main since this release

    Signed by john
    GPG key ID: 693AB15F91734B0C
    Downloads