The first release under constitution Article 3 (opum-ai/opum-agent
docs/reference/opum-project-constitution.md, ratified 2026-09-27): quest and
lore ship as a pair at one version number. lore 0.11.0 skips 0.10.0 to meet
it. Both are staged under the release-candidate dist-tag and qualified
together by opum-cli-e2e from registry installs. Only then does latest
move, quest first. It is minor rather than patch for two reasons. quest init
and quest agents gain opum-quest plugin detection and update. And a bare
quest agents --check in a Claude-only or Gemini-only project now exits 6
where it used to exit 0 (QCLI-373, below). Nothing else changed in any
command, flag or envelope.
Added
quest initandquest agentsdetect the opum-quest marketplace plugin
(QCLI-371, with QCLI-378 to QCLI-384 closing parity with lore-cli). When the
Claude or Codex target is selected, they readclaude plugin list --jsonor
codex plugin list --jsonand report the plugin as installed, disabled, not
installed or not detectable, with a remedy. Neither ever installs or enables
it.quest agents --update-instructionswith an explicit--targetupdates
an installed plugin and converges on the marketplace, and the skill is drawn
from the marketplace source so the two copies cannot drift apart. The rules
match lore-cli's:- Claude scopes rank managed > local > project > user > synced. Between two
rows of one scope, the deeperprojectPathdecides (QCLI-379). A managed
row decides and is never updated (QCLI-381). - A scope that cannot be named as a plain token gets no command at all:
the update is reported not run, and the remedy is prose (QCLI-383). A
token must start with a letter or digit (QCLI-382). - Every not-run report carries an
updateDetail, and the Codex wording
follows how far the update got (QCLI-384). - Runtime text is stripped of ANSI, control and bidi/invisible format
characters before any output mode.--scopereaches a remedy or an
argv only as a plain token. Plugin output is read up to 1 MiB (QCLI-380,
QCLI-382). - The list deadline (15s) ends the runtime's whole process group, so a
grandchild holding the pipes cannot keepquestalive (QCLI-378).
QUEST_AGENT_PLUGINS_TIMEOUT_MSandQUEST_AGENT_PLUGINS_UPDATE_TIMEOUT_MS
override the list and per-step update deadlines.
- Claude scopes rank managed > local > project > user > synced. Between two
Changed
-
A bare
quest agents --checkno longer reports on the wrong file
(QCLI-373). With no--targetit still checks the Codex block, because the
Codex managed block tells CI to run it that way. But in a project whose
Quest block lives only inCLAUDE.mdorGEMINI.md, it now exits 6 and
names the--targetto use, instead of exiting 0 about anAGENTS.mdthat
was never the point. -
Publishing refuses without an opum-cli-e2e qualification receipt, and
publishes the qualified bytes (QCLI-366, QCLI-368). The publisher reads
receipts/quest/<version>.jsonfrom opum-cli-e2e'smainand refuses,
dry run included, unless it binds this version, commit, qualification run
and all seven candidate-bundle tarballs. The seven bundle tarballs are then
published byte for byte instead of a repack of the working tree. After
publishing, npm'sdist.integritymust match every bundle file.
.gitattributespins the platform packages' LICENSE and package.json to LF,
which the Windows runners' checkout had turned into CRLF. Release tooling
only. -
Publishing refuses unless lore is at the same version (QCLI-386,
constitution Article 3 clause 6). Both publishers read@opum-ai/lore's
package.jsononopum-ai/lore-cli'smain, dry run included, and refuse
on a mismatch or on any failure to read it.
scripts/qualification/version-parity.mjs --requireruns the same check on
its own. Release tooling only. -
A release is staged under the
release-candidatedist-tag, andlatest
moves in a separate step (QCLI-385, constitution Article 3 clause 5). Both
publishers now pass--tag release-candidateon everynpm publish, so
publishing leaveslatestwhere it was.scripts/promote-release.mjsmoves
latestafter opum-cli-e2e has qualified the staged lore/quest pair from
registry installs. It refuses unless all seven packages are staged at the
version, and it records every priorlatestbefore moving any tag. It moves
the platform packages first and the wrapper last. A failure part way through
restores the tags that run moved, and--rollback <record>restores all of
them. Release tooling only; no command, flag, envelope or exit code of
questchanged. -
Moving
latestrequires opum-cli-e2e's verdict on the staged pair
(QCLI-388).scripts/promote-release.mjsreads
receipts/pair/<version>.jsonfrom opum-cli-e2e'smainand refuses unless
it isQUALIFIED, names quest and lore at the same version, and was taken
from registry installs. Every recordeddistIntegritymust also match what
npm serves when the promotion runs. A missing or unreadable receipt refuses.
Rolling back is never gated. Release tooling only.
Fixed
task edit --if-revisionreports a stale edit as a conflict (QCLI-374).
A stale revision is now refused with exit 5 before the patch is folded. A
concurrent removal of the same value used to surface as an exit-6
validation miss rather than the conflict it is. Callers that retry on exit
5 now retry.scripts/publish-release.mjsno longer reports a locked Keychain entry as
a missing token (QCLI-349). It used to treat everysecurityfailure the
same way and print "no stored token found", which points at the wrong fix. A
non-interactive read that fails with exit 36 (errSecInteractionNotAllowed)
now says the entry exists and namessecurity unlock-keychain. Exit 44 still
says to create and store a token. Any other failure is reported as "could not
be checked". A real publish with the entry locked and no--otprefuses with
the unlock remedy. This is release tooling only; no command, flag, envelope
or exit code ofquestchanged.- The publisher's success line names what it actually verified (QCLI-350).
"@opum-ai/quest published and verified (N checks)" described
checks of the six platform packages against the receipt, not of the
wrapper. On 0.9.0 it printed while an anonymous read of the wrapper still
returned 404. The script now checks that the wrapper resolves for a consumer,
using the same read that gates the platform packages, before it prints
anything. The line then lists each object with the check that verified it.
A package the registry does not show yet is described as one of three
states (slow, staged, never landed) instead of two. The wrapper is still
withheld whenever any platform package does not resolve. Release tooling
only. - The post-publish verification is now testable end to end (QCLI-304). It
moved out ofmain()intoverifyPublishedRelease, with every read
injectable except the wrapper's anonymous consumer read. A test now makes the
0.7.1 failure happen through that real read: the wrapper's write succeeded
but the public packument does not list the version yet. It shows that no
success line prints until a plain read lists the version. The runbook
publish section now says what "verified" means. Release tooling only. - A version bump now fails fast if
bun.lockstill pins the old platform
versions (QCLI-292). CI'sbun install --frozen-lockfiledoes not notice
at bump time, because the new version isn't on the registry yet. It failed
only after publish, on an unrelated pull request (the 0.6.2 breakage). The
newlockfile_pinssource gate (bun run check:lockfile) compares the six
pins topackage.jsonand namesbun installas the fix. Release tooling
only.