Repository navigation
Releases
JAM v1.8.0
JAM v1.8.0 — asking a command what it does, and being told which limit was hit
What changed
--help no longer runs the command. Measured on a real machine: a person
typed jam setup --help expecting documentation. The setup wizard ran. It
re-registered JAM with two hosts and moved the recorded registration from one
version to another, while they were reading what they thought was a description
of what the command would do.
The cause was narrow and the reach was not. --help was recognised only as a
command in its own right, so a help flag arriving after a subcommand was just
another argument and fell through to the handler. Every mutating path in the
dispatcher was reachable this way:
jam setup --help ran the wizard
jam update --help ran the update
jam refresh --help re-registered
jam uninstall --help removed the registration
jam auth login --help started the credential flow
jam runtime use … --help rewrote the runtime selection
jam jira write-apply … --help applied the plan
The guard is now in the dispatcher, before the switch, and it looks at every
argument rather than the first. The launcher answers runtime before dispatching,
so it has its own.
There is deliberately no per-command list. A list of commands that must be
protected is a list that will be missing the next command somebody adds; the
dispatcher is the one place all of them pass through.
Completeness says which limit was reached. Three situations were collapsing
into two codes, and two of those readings were wrong:
a preview that stopped at page one PARTIAL_API_RESPONSE (wrong)
a complete walk that hit the page cap OUTPUT_BUDGET (wrong)
a result larger than the token budget reported in notes (right)
PARTIAL_API_RESPONSE says Jira answered with less than it was asked for. A
preview is the caller getting exactly what they asked for — one page, on
purpose. OUTPUT_BUDGET says something was dropped to fit a size; the page cap
is about how far the read walked, and nothing was trimmed.
They now have their own names, and the meta carries what a caller needs to act:
PREVIEW_LIMIT one page by request → ask again with scope="complete"
PAGINATION_LIMIT the safety cap → narrow the JQL
moreAvailable is there more after what you got
returnedCount how many you got
The third was already right and is now pinned: a result can be complete and
still be larger than you want, and saying so must not turn complete false.
Pagination stays inside JAM. No continuation token reaches the caller
through any field. Handing one out would move paging to the agent, which is the
boundary this exists to hold.
Gate
npm test 582 pass · 0 fail
npm run release:check clean
CI ubuntu + windows, node 20 and 22, all green
New fixtures:
help-no-side-effect.test.ts 31 every mutating dispatcher path × --help and -h,
asserting the mutating modules were not called
completeness-semantics.test.ts 7 each situation separately, both at once, and
"no continuation token reaches the caller"
What an independent review checked and did not find
The review looked for a way past the help gate through argument order, through a
subcommand parsing its own flags first, and through the launcher path. It found
none: the gate runs before the switch, and the launcher's own runtime group has
a matching one. It also confirmed no continuation token can escape through a
nested field, and that PREVIEW_LIMIT and PAGINATION_LIMIT cannot be confused
— a preview caps pages at one, so the pagination flag is structurally
unreachable there.
Install / Upgrade
jam updateMoves this machine's registration to the published release. It does not re-plan
the project binding — that is jam setup's job.
First run on a machine with no JAM:
npx --yes @jam-mcp/bootstrap@1.8.0 setup --agentAgent setup
Unchanged from 1.7.0.
npx --yes @jam-mcp/bootstrap@1.8.0 setup --agent
jam status --jsonCompatibility
Node >= 20. The three packages ship in lockstep at 1.8.0 and depend on each
other at that exact version.
No state change on disk. Nothing in ~/.jam is read or written differently.
One behaviour change a caller can see. A command that carries --help or
-h now prints usage and exits 0 instead of running. Any script that relied on
a help flag being ignored will stop doing the thing it was doing — which is the
point, and is the safe direction to fail in.
Two IncompleteReason values are new — PREVIEW_LIMIT and
PAGINATION_LIMIT — and two CompletenessMeta fields are added,
moreAvailable and returnedCount. A consumer that switches exhaustively on
reason will see values it does not know. The old values are not removed;
searches that used to report PARTIAL_API_RESPONSE or OUTPUT_BUDGET for these
two situations now report the new ones instead.
Verified
npx tsc --noEmit green
npm test 582 pass / 0 fail
npm run build green
npm run release:check consistent across manifests, code and docs
CI pr-title + ubuntu/windows × node 20/22, all green
Behaviour, in the suite rather than against a live site — each mutating
dispatcher path is driven with the mutating modules replaced by spies, so the
assertion is that they were not called at all:
every mutating command × --help usage printed, no mutator called
every mutating command × -h usage printed, no mutator called
the same commands without a flag the command runs as before
jam help / --help / -h unchanged
Not run against real Jira: this release changes no Jira interaction. The
completeness work was verified against a fake Jira that pages, which is where
the page-cap and preview boundaries actually live.
Known limitations
The two new meta fields are optional in the type. moreAvailable and
returnedCount are always populated today, because one place builds a search
meta and it fills both unconditionally. The type does not enforce that. A caller
seeing undefined cannot distinguish "nothing more" from "not reported", and a
second construction site could reintroduce the ambiguity silently.
Usage text goes to stdout from the server and stderr from the launcher. The
launcher holds stdout for the MCP protocol on every path; the server has always
written usage to stdout. This release did not unify them.
The reported incident was not reproduced. Re-running jam setup --help
against the broken build would change this machine's registration again. What
was verified is that the old dispatcher structure routes setup --help to the
wizard, and that the new one does not.
MCP/CLI parity was not audited end to end. The --help path is closed on
the CLI side only.
JAM v1.7.0
JAM v1.7.0 — writing Jira when the tools are unreachable
What changed
jam jira write-plan and jam jira write-apply. Reads got a shell address
in 1.4.6 and writes did not. The gap shows up in exactly the situation the reads
were opened for: a session whose MCP registry came up in a failed state cannot be
shown the tools — Claude Code has no reload surface for a running session — while
JAM, its credentials and its Jira access are all fine. That session could read
Jira and could not change it.
jam jira write-plan --operation <op> [--key KEY] --input '<json>'
jam jira write-apply <planId>Nothing here implements a second write path. Both call the same planWrite /
applyWritePlan the MCP tools call, with the same deps, project binding,
whitelist and normalized error codes. The two-call contract is not relaxed for
the shell: planning changes nothing, and apply takes a planId and has no flag
through which a payload could arrive. There is deliberately no jam jira comment
or jam jira close — a shortcut past planning is what the write plane exists to
prevent, and a terminal does not change that. issue.create goes through the
same write-plan rather than getting a command of its own.
Plans now live in a file, shared by both transports. They lived in the server
process, and the ADR said why. The same section named the condition for
revisiting — "if plans ever need to outlive a process" — and write-plan and
write-apply are two processes.
before in the server process, in memory
after ~/.jam/write-plans/<planId>.json, read by MCP and the CLI alike
Sharing the store is the point rather than a side effect: a plan made through
the tools can be applied from the shell after the tools stop answering, which
is the case this release exists for. Single use moved to the filesystem — take
claims a plan by renaming it, so two concurrent applies cannot both hold one.
For comment.add the alternative is two comments.
Integrity is re-derivation, not a signature. Apply reads the plan's own
recorded input, derives the mutation from it again, and refuses with the new
JAM_WRITE_PLAN_TAMPERED when the stored mutation is not what that input
produces. Editing the file to smuggle a different write means editing the input
to match — which is asking JAM to plan that write. The whitelist runs again too,
so an edited input cannot reach a field planning refuses. The check runs after
the revision check: a moved issue is a conflict, not tampering.
docs/decisions/adr-jira-write-plane.md §"Where plans live" is rewritten rather
than quietly contradicted, and it records what was lost as well as what was
gained.
What did not change
The MCP surface. Five tools, the same names, the same inputs, the same meta
semantics: jira_search, jira_context, jira_full, jira_write_plan,
jira_write_apply. The operation set and both whitelists are untouched, and so
is every existing error code.
JAM still has no Execution Mode. That axis belongs to ASC, and a Jira write still
travels ASC's decision path.
Install / Upgrade
jam updateMoves this machine's registration to the published release. It does not re-plan
the project binding — that is jam setup's job.
First run on a machine with no JAM:
npx --yes @jam-mcp/bootstrap@1.7.0 setup --agentAgent setup
Unchanged from 1.6.0. The agent entry points are the same:
npx --yes @jam-mcp/bootstrap@1.7.0 setup --agent
jam status --jsonAn agent that has just registered JAM and cannot yet see the tools now has a
complete shell path — reads since 1.4.6, and writes as of this release.
Compatibility
Node >= 20. The three packages ship in lockstep at 1.7.0 and depend on each
other at that exact version.
One state change on disk. Plans are now files under ~/.jam/write-plans/,
created 0600 in a 0700 directory. Nothing else in ~/.jam is touched, and
plans still expire in ten minutes and are still single-use. A plan made by 1.6.0
cannot exist across an upgrade — it was in a process that has since exited — so
there is nothing to migrate.
Verified
npx tsc --noEmit green
npm test 538 pass / 0 fail / 4 skipped (542)
npm run build green
npm run release:check consistent across manifests, code and docs
CI pr-title + ubuntu/windows × node 20/22, all green
Against real Jira, on a dedicated health-check issue in the bound project — no
team ticket was touched:
write-plan planned, no mutation
write-apply from a second process applied · verified: true
same planId again JAM_WRITE_PLAN_NOT_FOUND, directory empty
plan, move the issue, then apply JAM_WRITE_CONFLICT
edit the stored mutation, leave input JAM_WRITE_PLAN_TAMPERED, nothing reached Jira
read → plan → apply, no MCP at all applied · verified: true
The last line is what this release exists for.
Known limitations
JAM_WRITE_UNCERTAIN was not exercised. Its trigger is an ambiguous
transport failure — a request that may or may not have landed — and that cannot
be produced against real Jira on demand. Only its code path was read. This is
recorded rather than implied because it is the failure whose handling matters
most: retrying it is what turns one comment into two.
--input JSON does not survive PowerShell quoting.
PowerShell --input '{\"text\":\"...\"}' → "--input must be a JSON object."
Git Bash --input '{"text":"..."}' → fine
Not specific to JAM — the same happens to any CLI taking a JSON argument — but
it is a real obstacle in the environment this feature is most needed in, since
the sessions that lose their MCP tools are often on Windows. Use Git Bash, or
match PowerShell's quoting. A --input-file or reading the object from stdin
would remove the problem and is a candidate for a follow-up; neither was added
here to keep this release to the change it describes.
A mutation is now serialized where the machine's user can read and edit it.
That is a real reduction from "forgery is impossible" to "forgery gains
nothing", and it is the price of a write path that survives its transport. See
the ADR for why a signed token was still not the shape.
JAM v1.6.0
JAM 1.6.0 answers to the same lifecycle words as ASC: status, refresh and
uninstall join setup, update and runtime.
What changed
jam status. The first place to ask what is going on. It isdoctor's
user-facing role under the name both products use, and it does not
reimplement the judgement: the same health gate and the same per-axis
verdicts — credentials, project binding, runtime, registration, live toolset,
Jira authentication, Jira project access — answer here.jam doctorkeeps
working and names its replacement.jam refresh. Keeps the version and re-registers what this build owns.
What goes stale in JAM is a registration line, andupdateis for when a
newer release exists;refreshis for when the line drifted from the build
that is already here. Its scope stops there: the project binding,
~/.jam/config.yaml, the credentials and every.jira-agent/project.yaml
are outside it.jam refresh checkandjam refresh planchange nothing.jam uninstall. Removes the MCP registrations JAM wrote and the global
launcher when one is installed. It keeps~/.jamin full — the project
bindings, the runtime choice — and the credentials in the OS secret store.
There is no purge: nothing yet needs a command that destroys those.- Help is organised by what a person is doing — Lifecycle, Jira,
Authentication, agent entry points — withjam servenamed for what it is:
the host's launch command, not something a person types. - Nothing is removed before its replacement is verified, in refresh as in
update, and a registration JAM could not read back is never reported as done.
What did not change
JAM has no execution mode. It is the Jira access layer: no session model, no
approval path, no guard. A Jira write still travels ASC's decision path and
lands through jira_write_plan / jira_write_apply. The two products share a
vocabulary, not a release train — ASC 0.8.0 and JAM 1.6.0 are independent.
Install / Upgrade
jam updateFrom nothing:
npx --yes @jam-mcp/bootstrap@1.6.0 initAgent setup
The canonical entry is unchanged:
npx --yes @jam-mcp/bootstrap@1.6.0 setup --agentUpdating an already-configured machine is jam update; making its registration
match the build already here is jam refresh. Neither is setup --agent, which
re-plans the binding and everything else.
Compatibility
- Node 20 or newer, unchanged — that is what every manifest's
enginesdeclares.
No config or data format change. jam doctorcontinues to work, printing a deprecation line;jam statusis
the name to use.
Verified
- CI: build, tests, release checks, packaging, packed smoke.
refreshis checked to leave a bare entry alone, to skip a host with no JAM
entry, to remove before re-registering, and to fail rather than claim success
when the read-back still shows the old pin.uninstallis checked to touch no path under~/.jamand to run no removal
of the person's files.
Known limitations
- Published acceptance against the 1.6.0 artefact runs after publishing.
refreshmeasures registrations through each host's own CLI; a host whose
CLI is not on PATH is reported as unavailable rather than assumed empty.
JAM v1.5.0
JAM 1.5.0 adds jam update: it moves this machine's registration to the
published release without re-running setup.
What changed
jam update. What goes stale in JAM is a line, not a directory — the host
launches JAM from a registration carrying an exact pin, and a newer JAM on the
registry changes nothing until that pin moves. Right after 1.4.6 was published
this machine measuredregistration: HOST_REGISTRATION_STALE (registered 1.4.5), and the command that fixed it wasjam setup --agent, which also
re-plans the project binding, credentials and everything else.jam update
does not call it. It proves the new build answers, re-registers the entry at
that version, reads the registration back, and runsdoctor— which is
config, credentials and one live read.- The order never removes first. The published build is verified before the
registration is pointed at it, so a failed update leaves the machine running
what it was running. - A host with no
jamentry is left alone. Registering JAM somewhere it was
never registered is adoption, andjam setupis where a person says they want
that. - The registry not answering is
UNKNOWN, never "up to date". - A registration that lands but is not healthy is rolled back to the pin that
was there. jam update check --jsonandjam update plan --jsonchange nothing.
Install / Upgrade
jam updateFrom nothing:
npx --yes @jam-mcp/bootstrap@1.5.0 initOptional persistent command:
npm install -g @jam-mcp/launcher@1.5.0Bindings, runtime selection, credentials and ~/.jam/projects.yaml are untouched
by an update.
Agent setup
The canonical entry is unchanged:
npx --yes @jam-mcp/bootstrap@1.5.0 setup --agentUpdating an already-configured machine is jam update, not setup --agent.
JAM updates JAM only — ASC has asc update, and neither calls the other.
Compatibility
- Node 20 or newer, unchanged. The five-tool MCP surface is unchanged.
- Additive CLI only: a new
updatecommand. Nothing existing changed shape. hostRegistrationaccepts a version, so a registration can name a release
other than the running build's — the rest of the argv is the line setup writes.
Verified
- Build, launcher + server tests,
release:check, pack and tarball smoke. - Ten new tests hold the order: check runs nothing, a pin that does not answer is
never registered, remove precedes add, the read-back is what reports success, a
failingdoctorrolls back, and setup is never invoked. - Real machine: 1.4.6 → 1.5.0 through the published artefact, with the project
binding, credentials and runtime mode compared before and after.
Known limitations
- Downgrade and pinning a specific version are not implemented.
- A bare global entry updates the global install and keeps the entry; rolling
that back is a re-install, and the command says so rather than pretending. - Live
issue.createremains unexercised, as in 1.4.6.
JAM v1.4.6
JAM 1.4.6 lets a session read Jira before it can see the tools. A session that
registers JAM cannot be shown the tools it just registered — Claude Code has no
reload for a running session — so jam jira search | context | full addresses the
same reads to the shell. The five-tool MCP surface is unchanged.
What changed
jam jira search <jql> [--scope preview|complete],jam jira context <KEY>…
andjam jira full <KEY>…. Each calls the same application function its MCP
tool calls, through the same deps, with the same field and pagination policies
and the samemeta—complete,evidenceScopeandlimitationsincluded.
Nothing here re-implements a read, and nothing here is a second source of
truth.- A failure is the same normalized error code the tools return.
error.code,
not a stack trace, so a caller reads one contract rather than two. - stdout stays one JSON document. Usage and diagnostics go to stderr, and an
invalid--scopeis refused before any request reaches Jira. - Reads only. Writing stays behind plan-then-apply, where a person's gate is.
Why
An agent that had just installed JAM was told to wait for its next session. What
it did instead was the substitution JAM exists to prevent: it answered Jira
questions from the code host, the git log and project documents. The tools being
unreachable for one session is a host constraint; answering from the wrong source
is a correctness failure, and this closes it.
Install / Upgrade
npx --yes @jam-mcp/bootstrap@1.4.6 initOptional persistent command:
npm install -g @jam-mcp/launcher@1.4.6Existing users: re-run setup. Bindings, runtime selection, host registration and
credentials are untouched.
Agent setup
The canonical entry is unchanged:
npx --yes @jam-mcp/bootstrap@1.4.6 setup --agentCompatibility
- Node 20 or newer, unchanged. The five-tool MCP surface is unchanged — same
names, same input schemas, same results. - Additive CLI only: a new
jiracommand group. Nothing existing changed shape. - No GitHub/GitLab runtime dependency.
Verified
- Build, launcher + server tests (488 passing),
release:check, pack and tarball
smoke — green locally and on CI (Ubuntu/Windows × Node 20/22). - Six new tests fix the shape: one JSON document, a multi-word JQL staying one
query, keys read as given,--scopepassed through and a bad one refused
before any read, a normalized error payload, and usage on stderr. - Read a real Jira issue through
jam jira searchfrom a shell in a session
whose MCP tools were registered afterwards. Zero mutations.
Known limitations
- Writes are not addressed to the shell. Creating or updating an issue stays
a two-step plan-then-apply through the MCP tools, where the person's gate is. - This does not make MCP tools appear in a running session. It is the read,
reachable another way, for the session that cannot see them yet. - Live
issue.createremains unexercised, as in 1.4.5, for the same reason:
an unwanted issue on a real board is not worth a disposable fixture.
JAM v1.4.5
JAM 1.4.5 makes a Jira reference survivable. Every issue now carries issueId
— Jira's immutable id — beside key, and statusCategory beside status, so
an agent can hold a reference that does not move and can tell whether something
is finished without matching localized status text. It also states the rule
that was missing: a Jira issue key is minted by Jira and is never predicted.
The five-tool MCP surface is unchanged. Everything here is additive output and
costs no extra Jira request.
What changed
issueIdtravels withkey. At all three read levels, and on nested
references — parent, subtasks, and both sides of an issue link — wherever
Jira supplies one. A key is the current human- and integration-facing
locator and Jira can move it to another issue;issueIdis the issue.
Nothing is fabricated: a payload without an id leaves the field absent
rather than empty.statusCategorytravels withstatus. Jira's own machine-readable
category key, passed through unchanged.statusis a workflow-defined,
localized name, so matching it against words is wrong in some project or
some language. JAM does not rename Jira's values, does not derive one from a
status name, and does not turn it into a verdict of its own.- Issue-key prediction is forbidden, and gated.
AGENTS.mdand
CLAUDE.mdcarry a mirrored "Jira issue keys" section: never synthesize,
increment, predict, reserve, or infer the availability of a key; positively
resolve an exact key against live Jira before reusing it; a key that did not
resolve is unusable and is not evidence the number is free.
release:checkfails if any of those three claims leaves either file. - Writes are pinned to the issue, not to the string. A plan records the
target'sissueId; apply asks the identity question before the revision
question, and again on the verification read. A key that has come to name a
different issue stops withJAM_WRITE_CONFLICTbefore anything is written.
A direct read returning an issue with no canonical id is refused
(PARTIAL_RESULT) rather than carried into a plan; list reads are
unaffected. - Both write receipts report
issueId,issue.createincluded — taken
from the verification read that was already happening. - No extra Jira traffic.
idandkeyare properties of the issue
resource rather than fields, andstatusCategoryarrives inside the
statusfield that was always requested. No nested reference is ever
fetched to fill in an id.
The reasoning is recorded in
ADR: Jira reference integrity.
Install / Upgrade
npx --yes @jam-mcp/bootstrap@1.4.5 initOptional persistent command:
npm install -g @jam-mcp/launcher@1.4.5Existing users: re-run setup. Bindings, runtime selection, host registration
and credentials are untouched.
Agent setup
The canonical entry is unchanged:
npx --yes @jam-mcp/bootstrap@1.4.5 setup --agentCompatibility
- Node 20 or newer, unchanged. The five-tool MCP surface is unchanged — same
names, same input schemas. - Additive JSON only:
issueIdandstatusCategoryon read results and on
nested references,issueIdon the plan and apply receipts. A consumer that
ignores them sees the contract it saw before. - No GitHub/GitLab runtime dependency. JAM does not create branches, read
commits or manage pull requests, and gains none of that here.
Verified
- Build, 39 launcher + 493 server tests,
release:check, pack and tarball
smoke — green locally on Windows and on CI (Ubuntu/Windows × Node 20/22). - Live read acceptance against a real Jira project, read-only:
issueId
equals Jira's raw id and is not the key;statusCategoryequals the raw
statusCategory.keyand is not the status name; the same issue read through
two endpoints reports the same id; 20 issues and 20 nested references all
carried identity in one Jira request. Zero mutations. - A key that does not resolve comes back in
meta.missingKeyswith
complete: false, and nothing in the result says or implies the number is
free. - The documentation gate was proven by removing a phrase and watching
release:checkfail. - Consumer compatibility: an existing MCP consumer's projection was driven
with real output from this release and is unchanged in shape — the additive
fields are ignored, which is the intended outcome.
Known limitations
- Live
issue.createwas not exercised. Creating an issue is not
reversible and a disposable fixture is not worth an unwanted issue on a real
board, so the create path's identity receipt is covered by unit and
integration tests rather than by a live mutation. - Nested identity is as good as Jira's payload. Where a response shape
does not carry an id for a nested reference, the field is absent. JAM will
not fetch the reference to fill it in — that would trade a correctness fix
for an N+1. - No Git or SCM enforcement, deliberately. JAM removes the premise the
mistake rests on and states the correct procedure where an agent reads it.
Guarding branches, commits and pull requests belongs to a control plane
above JAM, which can build it on the identity JAM now returns. - Subtask creation is still out of scope.
issue.createtakes no parent;
that is a write-capability change with its own schema and verification
questions.
JAM v1.4.4
JAM 1.4.4 makes project setup deterministic when a machine carries old
settings, and stops "mixed" credentials from reading as a fault. Two field
reports drove it: an operator had to hand-edit ~/.jam/projects.yaml before
--project would take effect, and a working credential split across the OS
store and one exported token was investigated as if it were broken.
What changed
-
--project <KEY>decides the run. It already beat a personal binding,
JAM_PROJECT_KEYand a legacy preset entry; that is now pinned by tests,
and a stale binding is repaired through the ordinary replace plan. Nothing
has to be deleted by hand first. -
The repository's committed key is never overwritten silently. It used
to win over--projectwhile being reported asexplicit, so the flag
looked honoured when a different key was in use. A disagreement now stops:{ "status": "user_action_required", "code": "JAM_PROJECT_KEY_CONFLICT", "requested": { "key": "PROJECT", "source": "explicit" }, "existing": { "key": "OTHER", "source": "repository" } }No change is applied over a conflict, in any entry point. Which Jira
project a repository belongs to is the team's decision. -
Key provenance is reported. Sources are
explicit,repository,
env,bindingorpreset, and a plan carriesproject.keySource. -
Credential sources, per field.
describe()andauth status --json
carry asourcesmap naming which source suppliedJIRA_BASE_URL,
JIRA_EMAILandJIRA_API_TOKEN. Names only — no value is ever printed. -
doctor --jsongains a per-axisdiagnosis:credentials,
projectBinding,runtime,registration,liveToolset,
jiraAuthentication,jiraProjectAccess. A failure is attributed instead
of guessed at, andmixedis aWARNINGon the credentials axis — never a
failure on its own. Whether Jira accepts the credentials stays on its own
axis.
Install / Upgrade
npx --yes @jam-mcp/bootstrap@1.4.4 initOptional persistent command:
npm install -g @jam-mcp/launcher@1.4.4Existing users: re-run setup. Bindings, runtime selection, host registration
and credentials are untouched — a stale binding is repaired in place.
Agent setup
The canonical entry is unchanged; --project is what became reliable:
npx --yes @jam-mcp/bootstrap@1.4.4 setup --agent --project PROJECTCompatibility
- Node 20 or newer, unchanged. The five-tool MCP surface is unchanged.
- Additive JSON:
project.keySource,requested/existingon a conflict,
sourceson credential descriptions,diagnosison doctor. - One new stop:
JAM_PROJECT_KEY_CONFLICT. It fires only where a run
previously used a different key than the flag asked for.
Verified
- server 475 + launcher 39 tests, typecheck, build,
release:check, pack and
tarball smoke — green locally on Windows and on CI (Ubuntu/Windows ×
Node 20/22). - Live matrix against the published artefact: stale binding replaced
OLDKEY→ the requested key; a disagreeing repository key stopped with
JAM_PROJECT_KEY_CONFLICTand zero changes; an agreeing repository key
reportedkeySource: repository. - Real project acceptance:
setup --agent --project <KEY>reachedready
with all seven diagnosis axes OK, including a live Jira read, and no
credential value in any output. - A mixed credential (store plus one exported token) reported
WARNINGwith
its per-field sources while the overall status stayedready.
Known limitations
- A repository conflict is deliberately not auto-resolvable: JAM reports both
sides and stops, and someone has to decide with the team.
JAM v1.4.3
JAM 1.4.3 makes the fresh Windows install actually work end to end. Every
defect found in a Windows field verification on a real project (2026-08-31) is fixed:
a fresh machine now always receives an MCP registration that runs, apply's
status stops contradicting itself, and Node 24 deprecation noise is gone from
every JSON surface.
What changed
- npx PATH contamination (release blocker) — under
npx --yes @jam-mcp/bootstrap@<version>, npx prepends its cache's
node_modules/.binto PATH, which contains ajamshim that vanishes when
npx exits. The "does this machine have a persistent jam" measurement ran
through that PATH, answered yes on every fresh machine, and setup then
registered a barejam servethat died immediately. The measurement now
strips package-runner entries (_npx,node_modules/.bin) first: fresh
machines get the always-runnable npx pin, and bare registration happens only
over a real persistent install. - Apply status honesty —
setup applyused to answer
already_configuredalongsidechangesApplied: true. Executed changes now
answerapplied. - DEP0190 removal — every
spawnSync(cmd, args, {shell: true})site
(host probes, live toolset, npm probe, launcher dispatch, dev scripts) now
validates bare tokens and joins the argv itself; cmd.exe metacharacters are
refused instead of concatenated.--jsonoutputs are clean of warnings.
Install / Upgrade
npx --yes @jam-mcp/bootstrap@1.4.3 initExisting users: re-run setup; a stale or dead registration is replaced in
place. Bindings, runtime selection and credentials are untouched — verified
in-place against a live operating setup.
Agent setup
No change to the canonical entry:
npx --yes @jam-mcp/bootstrap@1.4.3 setup --agentCompatibility
- Node 20 or newer, unchanged. The five-tool MCP surface is unchanged.
setup applymay now answerappliedwhere it previously (wrongly) said
already_configuredafter executing changes.
Verified
- server 455 + launcher 39 tests,
release:check, pack and tarball smoke —
green locally on Windows and on CI (Ubuntu/Windows × Node 20/22). - Live Windows reproduction of the npx-PATH defect: with a contaminated PATH
and no global jam,setup plannow proposes the npx pin (previously bare
jam serve). - Published-registry fresh setup on Windows with zero manual installs:
registerednpx --yes @jam-mcp/launcher@1.4.3 serveboots,doctor --json
reports ready on all three axes with 0 bytes on stderr, and the three read
tools answer live Jira withmeta.complete: true. - Non-mutating
jira_write_planexercised against live Jira (planId issued,
nothing applied); structured error path confirmed. - In-place re-apply over an existing operating setup:
already_configured,
binding/registration/runtime preserved.
Known limitations
- The GitHub Release for this version was created by the tag-triggered
workflow of its day; the successor pipeline separates publish from
finalization so acceptance always precedes the tag.
JAM v1.4.2
JAM 1.4.2 stops treating a healthy persistent installation as a defect. A bare
jam serve registration is now judged by measuring the executable it actually
runs, and setup's repair respects a persistent install instead of rewriting it
back to an npx pin.
What changed
- Bare-registration judgement — a
jam serveentry carries no version in
the host's listing, so JAM asks the executable (jam runtime status --json)
and compares that. Previously a bare entry was permanently misjudged stale. - Persistent-respecting repair — when the measured global launcher is
exactly this release, repair registers barejam serve; otherwise it keeps
the npx pin fallback. Repair no longer recreates the npx cache structure on
a machine that deliberately installed the launcher.
Install / Upgrade
npx --yes @jam-mcp/bootstrap@1.4.2 initExisting users: re-run setup once; a mis-judged bare registration is repaired
in place. Bindings, runtime selection and credentials are untouched.
Agent setup
No change to the canonical entry:
npx --yes @jam-mcp/bootstrap@1.4.2 setup --agentCompatibility
- Node 20 or newer, unchanged. The five-tool MCP surface is unchanged.
Verified
release:verifygreen on Ubuntu and Windows against Node 20 and 22 (CI),
including new regression tests for the bare-entry judgement.- Published via the Trusted Publishing workflow; registry verification and the
published smoke ran inside it.
Known limitations
- On a machine where
jamresolves only through a package runner's ephemeral
PATH, the persistent judgement could still be misled — fixed in 1.4.3. - This body was restored after the fact (2026-08-31) from the recorded
procedure and commit history; it claims no verification beyond those records.
JAM v1.4.1
JAM 1.4.1 makes jam doctor answer the question that actually matters on a
half-upgraded machine: not just "is this package current", but "what does the
host registration launch, and what does that process really serve".
What changed
- Doctor's three axes —
package(the runtime this machine would run),
registration(what the host CLIs have registered for this user, stale pins
detected by version), andlive(the registered command is actually
launched and its tool list compared). A stale pin serving an older tool set
no longer reads as ready. - Release smoke hardening: EPIPE fix in the published smoke, and the smoke now
verifies the real consumer path.
Install / Upgrade
npx --yes @jam-mcp/bootstrap@1.4.1 initExisting users: re-running setup repairs a stale registration; bindings and
credentials are untouched.
Agent setup
No change to the canonical entry:
npx --yes @jam-mcp/bootstrap@1.4.1 setup --agentCompatibility
- Node 20 or newer, unchanged. The five-tool MCP surface is unchanged.
doctor --jsonoutput gains theaxesblock (additive).
Verified
release:verifygreen on Ubuntu and Windows against Node 20 and 22 (CI).- Published via the Trusted Publishing workflow; registry verification and the
published smoke ran inside it.
Known limitations
- This body was restored after the fact (2026-08-31) from the recorded
procedure and commit history; it claims no verification beyond those records.