Skip to content

Quest CLI 0.7.0

Choose a tag to compare

@jeremy-newhouse jeremy-newhouse released this 27 Sep 18:57
· 291 commits to dev since this release

Version frozen 2026-09-15, in lockstep with @opum-ai/lore 0.7.0 -- the
pairing convention every release has held since 0.5.0, resumed after 0.6.2
broke it once deliberately. The date above is when this version was frozen
and the bump landed, not when it reached npm
; publication is a separate,
manually authorized step, and nothing in this file should be read as evidence
that it happened.

Minor, not patch, and one entry below deserves reading before you upgrade
rather than after. quest task pause now parks a task at "Paused" instead
of "Blocked", so anything matching on the literal string "Blocked" to
find paused work will stop matching.
No transition, flag or envelope shape
changed to make that true -- the configured value did. Everything else here
is additive: a new envelope field and two new optional flags, each inert when
not asked for.

Observed after release, 2026-09-15, reported by opum-cli-e2e from their
0.7.0 qualification matrix (their PR #154):
that warning was published and
the best-placed consumer still missed it. Their 60-project-lifecycle suite
pinned the literal "Blocked" and six rows failed against a conformant quest,
while their 40-cross-product suite read the value live and absorbed the
same release without moving a row. Their fix binds the suite to what
task status-flow declares. A changelog warning is not a gate; a consumer
that binds to the CLI's own declaration does not need one.

Added

  • Every success envelope now carries contractVersion: 1, a new field
    distinct from schemaVersion (which stays the outer envelope-wrapper
    version). contractVersion is a single global counter, not a per-command
    one: it moves whenever any command's data payload shape changes in a way
    an existing decoder would misread, so a consumer has one field to check
    rather than needing to track ~30 commands independently (QCLI-289).

    This exists because 0.6.2's QCLI-264/QCLI-265 envelope-shape unification
    (data.task becoming bare data, among others) shipped with schemaVersion
    unchanged at 1 on both sides of the break -- reported downstream by host
    mbpm2, whose own integration suite passed 0.6.2 validation and then broke
    anyway, because nothing distinguished the old shape from the new one.
    That break predates this field and stays undetectable by it:
    contractVersion starts at 1 in the release that introduces it rather
    than being backdated to claim a transition that had no signal at the time.
    Anything relying on the pre-contractVersion shape needs to keep doing
    what it already does today (a defensive read of either shape); this field
    only protects against the next shape change, not the last one.

    What contractVersion does NOT cover. Stated here because a field that
    says what it covers and not what it omits has the same defect it was added
    to fix -- and because 0.7.0 itself contains two changes it does not signal.
    Verified by probing every envelope family the 0.7.0 binary emits, not read
    off the source:

    • Error envelopes carry no contractVersion, by design. An error
      envelope is {error_type, message, hint?, input?, principal} and carries
      no envelope metadata at all -- no schemaVersion and no kind either --
      so its absence here is the existing design, not an oversight. Do not
      infer "contractVersion absent ⇒ pre-0.7.0 shape" from an error envelope;
      that rule holds only for success envelopes. Errors are discriminated by
      the frozen exit-code taxonomy (§3) instead, which is stable and needs no
      version field. This is deliberate and will not be revisited quietly: the
      error envelope is specified as a closed key set, so adding a key to it is
      a breaking change for any consumer validating it strictly -- and at least
      one does, which is precisely why the field was added to success envelopes
      only.
    • Configured-value changes are not payload-shape changes. The counter
      moves when a data payload's shape changes in a way an existing decoder
      would misread. It does not move when a value inside an unchanged shape
      changes. 0.7.0 contains exactly such a change: quest task pause now
      parks a task at "Paused" instead of "Blocked", so a consumer matching
      the literal string "Blocked" stops matching while contractVersion
      correctly stays 1. Read contractVersion as "can my decoder still parse
      this?", never as "did anything I depend on change?" -- the second question
      is what a changelog is for, and this entry is the answer for this release.
  • quest task view <id> --max-notes N caps implementationNotes to the most
    recent N entries, adding a notesOmitted count (present whenever
    --max-notes is supplied, including 0, never otherwise). Additive and
    opt-in -- omitted, task view is byte-for-byte unchanged (QCLI-276, one
    piece of DEC-3, a shape agreed jointly with @opum-ai/lore's own
    lore context budget work). The rest of DEC-3's originally-scoped surface
    (a general --fields selector, --since/cursor note selection, retrieving
    one note by a stable id, field-level omission metadata) is deliberately
    deferred, not dropped -- tracked as QCLI-291.

  • quest task edit <id> --if-revision <rev> (and a per-item ifRevision on
    task edit-batch) lets a caller supply the revision it read earlier and
    have the edit refused, before any state change, if the record has since
    moved -- the same exit-5 conflict a concurrent write race already produces.
    quest task view <id> --json now returns that revision (additive field) so
    a caller has something to capture. Omitted, task edit is unaffected
    (QCLI-277).

Fixed

  • A write conflict's actualRevision -- the one piece of data a caller needs
    to retry without a second read -- was silently discarded for every task
    write conflict, not only the new --if-revision case above: the internal
    helper that unwraps a mutation result threw a bare error with no payload.
    The documented retry protocol ("re-read the latest state and perform your
    own bounded retry") was correct advice that the CLI's own diagnostic didn't
    carry the means to follow. Now named in the diagnostic's input on every
    conflict, same exit code and message (QCLI-277).

  • quest agents --check --require-installed exited 0 for a managed
    instruction block whose content is current but whose embedded Quest CLI
    version string is stale (state: version-only) -- correct, deliberate
    behavior since QCLI-228, and already documented that way in this CLI's own
    guide and help text. The one place that still lied about it: the
    block's own embedded CI-hint sentence, written into every consumer's
    CLAUDE.md/AGENTS.md, which said only "current instructions exit 0, ...
    missing, drifted, or malformed managed instructions exit 6" with no mention
    of the version-only case -- so a reader (lore-cli, concretely, whose
    CLAUDE.md said Quest CLI 0.4.0 against an installed 0.6.0 for two minor
    versions) reasonably concluded CI caught version drift when it deliberately
    does not. The sentence now names the exemption explicitly; the exit code
    itself was never the defect (QCLI-284, DEC-4).

  • quest task pause <id> parked a task at status "Blocked" by default --
    a false signal read fleet-wide as "needs intervention," even when nothing
    was blocking the task. The default paused status is now "Paused",
    structurally unchanged (still separate from the To Do/In Progress/Done
    ladder, still reachable only via pause/start) -- a naming fix, not a
    new transition (QCLI-287).