Release v0.6.7
This release includes all changes since the previous version.
Installation
npm install langsys-js-react@0.6.7Changes in this release
0662742
build: block publish when origin/main has commits we lack
--force-with-lease was not protecting this script. The lease compares the
remote against our remote-tracking ref, and publish.sh fetches immediately
before pushing — so a colleague's pushed commit is already 'expected' when
the lease is evaluated, and the flag authorises the overwrite it is named
for.
Reproduced in a sandbox rather than inferred: with the fetch done and the
branch diverged, 'git push --force-with-lease' reported '(forced update)',
exited 0, printed no warning, and left the colleague's release commit
unreachable from main. Verified the guard fires on divergence, passes
after a rebase, and that the colleague's commit survives the rebase path.
Two accounts have push access to this repo, so this was reachable, not
theoretical. Guard is placed before ORIGINAL_VERSION is assigned so
handle_error aborts with nothing to roll back, and is commented as
load-bearing — 'redundant with --force-with-lease' is the exact reasoning
that would reintroduce it.
Reported by the langsys-js-svelte agent after the Vue binding hit it live.
ba1aed0
build: fetch before the unpushed-commits check
I previously flagged the stale ahead-check as benign because it can only
over-report. Over-reporting is the failure. Reproduced in a sandbox:
HEAD already published (pushed from another machine or a co-maintainer)
ahead(stale) = 1 -> script proceeds
fetch
behind = 0 -> divergence guard passes (correctly: no divergence)
amend + force-push
-> the already-published commit is rewritten, orphaning any tag or
provenance attestation resolving to that SHA
Nothing looks anomalous on that path — no colleague, no divergence, no
conflict — and the destroyed commit is your own. Fetching first makes the
check read reality: ahead(fresh)=0 aborts with 'No unpushed commits found'.
That abort is correct rather than a regression: this script embeds the
version bump by AMENDING HEAD, so amending an already-published commit is
precisely the operation that rewrites published history.
Verified all three cases against the new order: published-HEAD aborts,
colleague divergence still aborts, normal release still proceeds.
Reported by the langsys-js-svelte agent, who correctly pushed back on my
earlier assessment.
623f55b
build: run divergence guard before the unpushed-commits check
Diagnostic quality only — both orders are equally safe, since the fetch
sits above both checks and every dangerous state aborts either way. They
differ in one state: ahead=0, behind=1.
old: 'No unpushed commits found. Please make your changes and commit
them before publishing.' -- sends the operator to commit work
they do not have, and invites a junk commit to satisfy it
new: 'origin/main has 1 commit(s) you do not have ... git pull --rebase
origin main' -- names the real condition and the fix
Re-verified all four cases against the new order: normal release
proceeds, colleague divergence aborts via the guard, already-published
HEAD aborts via the ahead-check, and behind-with-nothing-to-release now
reports the useful message.
Suggested by the langsys-js-typescript agent while diffing the four
publish scripts.
9d0b735
docs: name the catalog cost of bare {key} in Translate markup
The %key% guidance explained the mechanism (JSX evaluates {count} before
the SDK's walker sees the text, so interpolation breaks) but framed the
damage as broken interpolation. That understates it.
Because the evaluated value is captured as part of the phrase, each
distinct value hashes to its own content block. Measured against the
shipped tokenizeElement/generateCustomId:
{count} via JSX %count% placeholder
"You have 0 items" 31ff32bd.. "You have {count} items" 88642c82..
"You have 1 items" 5aa5eef5.. "You have {count} items" 88642c82..
"You have 2 items" 89f09f5e.. "You have {count} items" 88642c82..
So a around a live counter registers a new content block per
tick. Nothing looks wrong while it happens — the base locale renders
correctly throughout — and it surfaces later as a Translation Manager
full of near-duplicate entries.
Broken interpolation sounds like something you would catch in testing;
this is specifically the class you would not. Worth stating plainly.
Surfaced by the langsys-js-svelte agent as an aside while running the
custom_id harness; both halves measured here, including that %key%
normalizes to canonical {count} and yields one stable id across values.
README.md, src/components/Translate.tsx.
2b73c7a
docs: close the same {key} catalog gap in
Translate.tsx got this in the previous commit; Phrase.tsx had the
identical defect and was missed. Both the prop doc and the component
JSDoc explained the mechanism ("a bare {n} in JSX is an expression")
without naming what it costs.
Phrase reaches the problem by a different route, so it needed its own
measurement rather than a copied claim. It keys on the encoded phrase
string from encodeRichText, not on a token array:
{n} via JSX "Based on 0 {m0o}reviews{m0c}" (new key per value)
%n% placeholder "Based on {n} {m0o}reviews{m0c}" (stable across values)
Same outcome as Translate, different mechanism.
JSDoc is worth more than usual on these two: it ships inside dist/ and
is what IDE hover renders, so for a consumer who never opens the README
it is the documentation.
Prompted by langsys-js-svelte, who found the same omission in
Phrase.svelte's JSDoc after fixing their README.
b46f59a
build: allow releasing when HEAD is already pushed
The script bumped the version by amending HEAD, so it required an
unpushed commit and aborted otherwise. That abort was correct given the
amend -- amending a published commit force-pushes a rewrite and orphans
any tag or attestation pointing at the original SHA -- but it turned an
ordinary habit, pushing work when you finish it, into a dead end. The
only ways out were a junk commit (which the comment there explicitly
warns against) or rewinding the remote. The abort was protecting the
amend; the amend is what needed to be conditional.
The fresh ahead-count now selects a strategy instead of gating the run:
ahead > 0 HEAD is local-only -> amend, force-push (unchanged)
ahead = 0 HEAD is published -> new "chore: release X.Y.Z" commit,
plain push, no force at all
Both guards are untouched and both still run before this decision: the
fetch, then the divergence check, then the count. The count being FRESH
remains load-bearing for the same reason as before -- a stale ref
over-reports and would route a published HEAD onto the amend path, which
the divergence guard cannot catch because behind is legitimately 0.
The bump itself can't be skipped: CI checks out the release tag and runs
npm publish, which reads package.json, so the tagged commit has to carry
the new version already. Tagging an untouched HEAD republishes the
previous version and fails.
Rollback needed no change -- HEAD@{1} is the pre-amend commit on one path
and the pre-commit HEAD on the other -- but its comment said "before
amend" and now says both.
f98a1bb
chore: release 0.6.7
Full Changelog
See the commit history for a complete diff of all changes.