8000
Skip to content

Tags: codenameone/CodenameOne

Tags

7.0.267

Toggle 7.0.267's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Drain the GC's grace pass as it walks, ending the overflow spiral (is…

…sue #5537) (#5573)

* Drain the GC's grace pass as it walks, ending the overflow spiral (issue #5537)

A deep game-tree search that #5563 saved from an EXC_RESOURCE kill came back
frozen instead: GC pauses growing longer and more frequent until they were
effectively continuous, with the simulator's footprint climbing to gigabytes
while the app retained nothing. Both readings are the same defect, and it sits
under the one #5563 fixed rather than beside it.

Every cycle the grace pass walks the BiBOP page registry and marks every object
allocated since the last cycle -- a fresh object may already be linked into the
live graph, so it and its subtree have to survive. How many that is depends on
the mutator's ALLOCATION RATE, not on the live set, and the pass pushed all of
them onto a fixed 65536-entry worklist before draining any of it. A worker
churning small short-lived objects produces several times that per cycle, so the
worklist overflowed as a matter of course.

Overflow is survivable -- the dropped entries are already marked and the belt
re-discovers their children -- but the belt is a full O(heap) rescan. It makes
the cycle several times longer, the mutator leaves proportionally more fresh
objects for the next one, and that one overflows for certain. The collector never
returns to its fast path. Every symptom on the issue follows from that single
loop: the original kill by the iOS per-process ceiling, the frozen app once
#5563's pacing held the process under that ceiling and had to park the mutator on
nearly every allocation instead, and the simulator's climbing footprint where no
ceiling exists at all.

Both grace passes -- the page registry and the legacy table -- now drain when the
worklist reaches half capacity. That costs nothing the end-of-pass drain would
not have cost anyway, since the same objects are scanned, only sooner; what it
buys is a cursor that cannot run away. The drain runs outside the trusted window
(CN1_GC_TRUSTED_SUSPEND/RESUME, added because BEGIN/END save and restore a
block-scoped local and so cannot express a hole inside a walk): a drain follows
child words out of arbitrary mark functions, which is precisely what the resolve
guard exists for. A _Static_assert pins the remaining assumption -- that a whole
page of slots fits above the drain threshold -- so raising CN1_BIBOP_PAGE_SIZE
fails the build rather than quietly restoring the spiral.

Measured on a repro of the reporter's shape (worker thread, tree search, live set
of one path). Realistic version, no ceiling: peak footprint 6.2GB -> 231MB, cycle
time 6ms->750ms -> a flat 6ms, and 30% more nodes searched. Heavier version under
a 512MB simulated ceiling, which is the device case: 77 of 150 cycles overflowed
and the mutator parked 72 times -> 0 of 440 and no parks, 10.2s -> 6.8s, with
174MB of headroom left instead of 64MB.

GcOverflowSpiralIntegrationTest guards it, asserting zero overflow cycles under a
simulated ceiling and that the pass actually reached its drain threshold (else the
first assertion would pass on a run that never allocated). Ablating the drain and
leaving everything else in place fails it with 77 overflows. Overflow cycles are
counted through a new env-gated [GC-OVERFLOW] tracer, and the count is taken with
an exchange on the existing flag so it reads once per cycle rather than once per
dropped push.

Not addressed here, and separate: off a per-process ceiling the pacing cap is
still a fraction of the HOST's free RAM, so on a RAM-rich Mac a sufficiently
extreme allocator can build gigabytes of garbage before anything stalls it. A
live-set-relative cap and an absolute cap were both measured and rejected -- each
cost 2-4x throughput, because a volume-cap park waits out a whole collection while
the footprint-based admission used under a real ceiling is both bounded and free.
Extending that admission to hosts with a footprint probe but no ceiling is the
right fix and needs its own benchmarking.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Keep the grace pass's periodic drain off the heap-rescan path

The first cut hung the Mac Catalyst screenshot suite: the collector never
finished a cycle, the EDT stacked up in the pacing park behind it, and the
DeviceRunner never reached its completion marker. The hang sample is
unambiguous -- the GC thread sits in gcMarkDrain called from the interleaved
drain this branch added, while the EDT sits in cn1PacingPark under
cn1BibopAlloc.

gcMarkDrain is not "drain the worklist". Every call to it also walks
allObjectsInHeap from index 0 and re-pushes every object already marked this
cycle, so that anything left marked-but-unscanned by an overflow gets its mark
function run. That is the right shape for the handful of calls a cycle makes,
and quadratic for a caller that drains PERIODICALLY: the grace pass drains once
per half-worklist, which turned one O(heap) rescan per cycle into hundreds.

Split the worklist loop out as gcMarkDrainWorklist and point the two interleaved
drains at it. The passes still end with a full gcMarkDrain, which is what closes
the fixpoint; nothing a periodic drain leaves behind escapes it.

The local guard could not see this and now can. A translated micro-benchmark
holds almost nothing in allObjectsInHeap, so an O(table) drain and a cheap one
measure the same -- which is exactly why this passed here and failed on a real
app. GcOverflowSpiralApp now retains a reference-carrying legacy population
(Object[] blocks; the rescan skips objects with no mark function, so an earlier
byte[] version of this fixture was free and proved nothing), and the VM reports
graceFullDrains: full drains taken while a grace pass is running. Two per cycle
is all a correct implementation makes, one to end each pass. Ablating the fix by
pointing the interleaved drain back at gcMarkDrain takes that from 262 across 133
cycles to 1277, and the new assertion fails.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

7.0.266

Toggle 7.0.266's commit message
ci: record browser syndication results

7.0.265

Toggle 7.0.265's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Gate against relying on ClassCastException, which ParparVM does not t…

…hrow (#5531) (#5532)

* Gate against relying on ClassCastException (issue #5531)

ParparVM's CHECKCAST is unchecked: BC_CHECKCAST expands to nothing and
BytecodeMethod.optimize() drops the instruction, so a failed cast hands the
wrong pointer to the next instruction rather than throwing. Code written to
catch the failure therefore behaves differently on iOS than on the simulator
and Android -- in the reported case StringToReal.parseDouble ran with a Double
as its String argument and died at cn1_intrinsics.h:90 reading
java_lang_String_value out of it, uncatchable from Java.

Making CHECKCAST throw would put a class check on every cast in every app, so
the rule is instead that our own code must not depend on the exception. Adds
CastSemanticsVerifier, which reports a CHECKCAST inside a try whose handler
catches ClassCastException or a supertype, and wires it into the PR CI Java 8
leg over JavaAPI, core, android and ios.

The rule is about the cast, not the handler: a catch(ClassCastException) with
no cast under it is fine, because an explicitly thrown ClassCastException
still propagates normally (java.util.AbstractSet.equals is that case). Casts
already guarded by an instanceof are recognised and never reported, so the
remedy the gate asks for satisfies it.

Findings are held against scripts/cast-semantics-baseline.txt -- a ratchet of
187 pre-existing sites, most of them casts that merely sit inside a broad
defensive guard. New code cannot add entries.

Fixes two genuine cases found this way:
- Purchase.getReceipts read a storage entry of any type as a List<Receipt>
- PropertyIndex.newInstance returned any newInstance() as a business object

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Regenerate the cast-semantics baseline from a fresh build

The baseline shipped in the previous commit was generated against a stale
maven/android/target/classes, so three AndroidImplementation methods that exist
in the source were missing from it and PR CI failed on code that was not new.

Rebuilds android + ios with -Pcompile-android and regenerates: nothing is
removed, the four Android findings are added.

Adds --require-all, used by CI, so a module that is not built fails the gate
instead of silently shrinking its coverage -- which is what let the stale build
through. Also records why maven/java-runtime (Ports/CLDC11) is deliberately
out of scope: it runs on a real JVM, where a failed cast does throw.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

7.0.264

Toggle 7.0.264's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Start the Maven Central → Cloudflare R2 migration, and cut the releas…

…e payload by 66% (#5497)

* Start the Maven Central to Cloudflare R2 migration, and cut the release payload by 66%

Sonatype rate-limits Maven Central consumption for commercial open-source
projects, which makes Central a product
8000
 availability risk rather than just a CI
one. This is phase 0 (shrink what we publish) plus phase 1 (dual-publish to R2
alongside Central).

Measured against the real published 7.0.258 (44 artifacts, 127 jars), a release
was 229.5 MB. It is now 76.9 MB. At ~65 published versions a year that is the
difference between 6 months and ~1.6 years of history inside R2's free tier, so
retention becomes a safety net rather than something the product depends on.

Extract the CSS compiler CLI from the Resource Editor

CSS compilation ran by shelling out to the 43.5 MB codenameone-designer
jar-with-dependencies -- the whole Swing editor, on every headless build. The
CSS engine (CSSTheme, ResourcesMutator, WebviewSnapshotter, PollingFileWatcher)
was already in css-compiler, but its NoCefCSSCLI entry point sets
strictNoCef=true and has no -merge/-l/-watch, so it could not replace the real
driver. CN1CSSCLI referenced no other Designer class, so it moves verbatim into
a new 28 KB codenameone-css-cli module, launched with `java -cp` from a resolved
classpath. The simulator gets that classpath via simulator.properties, the same
channel SourceChangeWatcher already uses, so no archetype or generated-pom
changes are needed and CSSWatcher still falls back for older projects.

capture.js moves to css-compiler as well: ResourcesMutator loads it via
getResourceAsStream but it only ever shipped inside the designer jar.

Freeze the deprecated Resource Editor, javase-svg and sqlite-jdbc

Excluded from publication and resolved on demand at a pinned version. Note that
<maven.deploy.skip> does not achieve this on its own: central-publishing-maven-
plugin runs with extensions=true, clears maven-deploy-plugin's executions, and
filters solely on excludeArtifacts.contains(getArtifactId()). Both are set --
excludeArtifacts for today, the module properties for the plain deploy path.

sqlite-jdbc also needed its dependency version pinned, because codenameone-javase
depends on it and is published; otherwise every released javase pom would point
at an artifact that was never uploaded.

Stop attaching five unconsumed jar-with-dependencies classifiers

javase, ios, windows, linux and parparvm published a shaded fat jar nothing
consumes -- verified across this repo plus BuildDaemon, BuildCloud, BuildClient,
OfflineBuilder and Deploy. The builders use the -bundle classifier, which is
unchanged. The fat jars are still built on disk because the bundle steps read
them; they are just no longer attached.

Publish to R2

maven/scripts/r2/ uploads the tree central-publishing already stages (standard
layout, four checksums, .asc) before it uploads to Central, so R2 succeeds even
when Central false-negatives. maven-metadata.xml is regenerated from the bucket
listing rather than the build, because a staging tree holds only the version
just built and would clobber the accumulated history.

Cloudflare caches 404s on the R2 custom domain, so CI polls are cache-busted; a
Cache Rule sets Status Code TTL 400-599 to no-store as the primary fix.

Also fix the Central confirmation poll, which looked for
"platform-feature-catalog" while the module has always been
"codenameone-platform-feature-catalog". On the false-negative path it would have
spun for 30 minutes and failed a release that actually succeeded.

Three CSS regression tests were orphaned in an Ant target CI never ran. They now
run under surefire -- and two were broken.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Address review feedback and fix CI

Fix the sqlite-jdbc pin, which did not actually pin (P1)

maven/javase/pom.xml declared <version>${project.version}</version> on the
dependency, which overrides dependencyManagement. Since the same change excludes
sqlite-jdbc from publication, every released codenameone-javase pom would have
requested a sqlite-jdbc that was never uploaded -- exactly the failure the pin
was meant to prevent. Dropping the child version lets the parent's
${cn1.sqlite.jdbc.version} apply; the effective pom now resolves 7.0.263.

Publish editor staging trees even when Central fails (P1)

The editor R2 steps had no status function, so they inherited the implicit
success() check and were skipped whenever the editor's Central deploy failed --
the exact outage R2 exists to survive. The three deploys now run with
continue-on-error so their staging trees reach R2 first, the uploads are
always(), and a final gate fails the job if Central rejected any of them.
Metadata is regenerated once at the end instead of after each editor.

Compare .sha1 sidecars, not artifact ETags (P2)

An object's ETag equals its MD5 only for single-part uploads, and aws s3 cp goes
multipart above 8MB. Comparing jar ETags would report every large artifact as a
conflict when a tag is retried. The sidecars are 40 bytes, always single-part,
and each uniquely identifies its artifact's content.

Fix the malformed java -jar in the CSSWatcher fallback

JVM options must precede -jar and the jar path must follow it immediately. The
pre-existing ordering put -Dcli=true where the jar belongs, so the JVM treated
that as the jar name. Only reachable on the legacy path now, but it never worked.

Python nits from the code-quality bot: @functools.total_ordering on
ComparableVersion, and an explicit namespace string instead of implicit literal
concatenation inside a list.

Fix CI

- CompileCSSMojoTest stubbed getDesignerJar(), which CompileCSSMojo no longer
  calls; it now stubs getCssCliClasspath(). All 308 plugin tests pass. This
  slipped through because the earlier local run used -DskipTests.
- Add the Codename One GPLv2 + Classpath Exception header to the relocated test
  sources, HeadlessTestSupport and capture.js. Their previous location was never
  checked, because the gate only inspects changed files.
- Make check-copyright-headers.sh rename-aware. PropertiesUtil kept its Oracle
  header across a pure move, but the Oracle-allowance check looked the file up at
  its new path in the base revision and failed. Moving a file must not force it
  to be relicensed, so renames are now resolved to their base path.

The remaining build-test (8) failure is an unrelated pre-existing flake:
MCPReleaseBuildGateTest and MCPLoopbackTransportOpenTest contend over
process-wide MCP state on fixed port 47899. Nothing under core-unittests or
com/codename1/mcp is touched here, and jobs 17 and 21 pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Do not let one publication destination's failure skip the others

Abort when the R2 head check itself fails (P1)

`|| true` turned every head-object failure into empty output, which the next
line read as "object not published yet". Throttling, an expired token or a
transient endpoint error would therefore silently disable the immutability guard
and let the copy overwrite an already-released artifact. Only a confirmed 404
now counts as absent; anything else aborts with the AWS error.

Keep later editor deploys running after an R2 failure (P1)

The previous commit stopped a Central failure from skipping the R2 upload, but
introduced the mirror of that bug: the R2 steps had no continue-on-error, so a
Game Builder R2 outage failed the job and GitHub's implicit success() guard
skipped the Signing Wizard and Settings *Central* deploys -- turning one
destination's outage into a partial release on the other.

Every publish step is now non-fatal with an id, every conditional step carries an
explicit status function, and a single gate at the end reports each destination
and decides the job's verdict. One destination failing can no longer skip the
other, and continue-on-error cannot mask a failure because nothing but that gate
determines the result.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Keep R2 a complete release when Central fails, and never advertise a partial one

Build the editors when Central failed but R2 succeeded (P1)

The editor deploys were gated solely on the core Central deploy, and they are the
steps that produce the editors' R2 staging trees. A genuine Central failure
therefore left R2 with the new plugin but no matching-version
codenameone-gamebuilder / -certificatewizard / -settings, which is exactly what
cn1:gamebuilder and friends resolve per version. The JDK 17 setup and all three
editor deploys now also proceed when r2_core succeeded.

Check the artifact when its .sha1 sidecar is absent (P1)

An interrupted recursive copy can leave a jar or pom behind without its sidecar.
The conflict check looks only at sidecars, so it read that as "not published" and
would have re-copied over the artifact. It now heads the artifact itself in that
case and says so. The upload still proceeds, deliberately: a completed release
always has both files, so artifact-without-sidecar means the earlier upload never
finished and the version was never advertised in maven-metadata -- finishing it is
the correct action, and now a visible one rather than a silent one.

Do not regenerate editor metadata after a partial upload (P2)

regen-maven-metadata.py discovers a version from any key under <artifact>/<version>/,
so running it after a partially-failed editor copy would publish an incomplete
release as <latest>/<release>. That step is now skipped when any editor upload
failed; the final gate still fails the job.

Verified against the live bucket: identical bytes re-upload idempotently, differing
bytes for a published version abort with exit 1, and the sidecar-absent path reports
the partial upload it is completing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Decide the release verdict in exactly one place

The core Central failure was already caught -- "Fail when release did not
complete" has no continue-on-error, its condition covers it, and every preceding
step is continue-on-error so it is always reachable. But having a second
verdict-deciding step in the middle of a workflow whose stated model is "one gate
at the end decides" is a maintenance hazard, and it reads like a hole to anyone
auditing the gate. It was in fact read that way in review.

Fold the core Central check into the final gate and delete the mid-workflow step,
so the invariant is now true rather than merely intended: nothing but that gate
determines whether the job is red.

Simulated across the outcome combinations: clean run passes; a false-negative
Central deploy rescued by the confirm poll passes; deploy and confirm both failing
fails; an R2 failure with Central fine fails; a single editor R2 failure fails.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Keep the legacy style editor reachable, and publish metadata only once a version is whole

Restore Component Inspector's Edit Style on a frozen designer (P2)

Freezing the Resource Editor broke a path I had not considered. editStyle() falls
back through codename1.designer.jar (which PrepareSimulatorClasspathMojo no longer
sets), then MavenUtils.findDesignerJarInM2 -- which looked for a designer matching
the *current core version*. With the editor pinned at a version that no longer
tracks core, that lookup could never hit, so on a CSS-less project the action just
told the user to open the designer first.

findDesignerJarInM2 now falls back to the newest designer actually present in m2,
ordered numerically so 7.0.263 beats 7.0.9. That keeps the action working wherever
a designer has been fetched, without making every cn1:run download 43MB to serve a
feature most projects never touch. CSS projects are unaffected either way: that
path opens the stylesheet in the IDE and returns before any of this.

Publish R2 metadata only after every artifact is in place (P2)

Core metadata was regenerated immediately after the core upload, which advertised
the new plugin version as <latest>/<release> before the editors existed. Since the
plugin resolves gamebuilder / certificatewizard / settings at its own version, a
consumer could upgrade to a version whose matching tools could not resolve, and a
later gate cannot retract metadata already published.

There is now a single regeneration at the end, gated on the core and all three
editor uploads succeeding, and the maven-metadata assertion moved there with it.
Metadata is what makes a version discoverable, so it is written last -- a failed
release leaves its artifacts orphaned but invisible, which is the recoverable
direction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Add copyright headers to the two files this branch modified

MavenUtils carried the NetBeans "To change this license header" placeholder and
MavenUtilsTest had none. Both predate this branch, but the gate only inspects
changed files, so touching them is what surfaced it.

Also refresh the findDesignerJarInM2 javadoc, which still described the designer
as a plugin dependency fetched on every build. That stopped being true when the
editor was frozen, and the comment was the reason the version-matching assumption
looked correct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Record that the legacy Ant Designer build does not compile

Review flagged that moving CN1CSSCLI and PropertiesUtil out of
CodenameOneDesigner/src breaks the Ant build, whose javac.classpath never
referenced the new Maven modules. The premise is right; the conclusion that this
branch broke it is not.

That build already could not compile on master. CN1CSSCLI there imports
com.codename1.designer.css.CSSTheme, and CSSTheme lives only in
maven/css-compiler/src -- not in CodenameOne/src, Ports/JavaSE/src or
Ports/JavaSEWithSVGSupport/src, and css-compiler appears nowhere in
nbproject/project.properties. So the classpath gap predates this branch by however
long ago the CSS engine was extracted; these moves add files to a list that was
already unsatisfiable.

Nothing exercises the path either: ant.yml runs Maven despite its name, no ant
check exists on PRs, and the BuildDaemon's three invocations of
CodenameOne/build.xml's `release` target -- the only caller of this file -- are all
commented out.

Rather than make an unverifiable change to a build that is already broken, unused
and untestable here, document the state so the next person does not have to
rediscover it, and say what repairing it would actually take.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Fix invalid XML: double hyphens inside the comment I just added

XML forbids "--" inside a comment. I pushed before validating, so the previous
commit left CodenameOneDesigner/build.xml unparseable. Second time this trap has
bitten in this branch; validate XML before pushing, not after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Never advertise a version whose upload did not finish

Gate discovery on a completion marker (P2)

Skipping metadata regeneration on the run that failed only protected that run.
Every later run rebuilds metadata from the same bucket listing, and discovery
treated any key under <artifact>/<version>/ as a whole version -- so the next
successful release would find the abandoned partial upload and publish it, possibly
as <latest>.

publish-staging-to-r2.sh now writes _cn1-upload-complete into each version
directory only after that directory's copy succeeds, and regen-maven-metadata.py
refuses to advertise a version without it, naming what it skipped. The marker is
deliberately not a Maven artifact pattern, so no resolver will ever request it.

Accept retries of non-latest tags (P2)

The confirmation asserted that the tag being built is <release>. Re-running an
older tag to repair it is legitimate, and metadata correctly keeps the newer
version as <release> -- so a successful repair was reported as a failure. It now
asserts discoverability (the tag appears in <versions>) and only reports on
<release> when this tag really is the newest.

Verified against the live bucket end to end: a complete upload is marked and
advertised; a version with files but no marker is skipped by name and stays out of
<versions>; re-running it completes the upload, writes the marker, and it then
appears with <release> moving to it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Do not publish editors to Central when the core release did not get there

Allowing the editors to build on the R2-only path (so R2 gets matching-version
editors) had a consequence I missed: the deploy command still carries its
*-central profile, and the editor resolves the just-built core from the local
repository, so its Central upload can succeed even when that core/plugin version
never reached Central. Central releases are immutable, so that leaves a permanently
unresolvable editor artifact there.

The editors now pass -DskipPublishing unless the core release actually reached
Central and dual publish is on. skipPublishing still stages the artifacts, so the
R2 upload is unaffected -- which is the whole point of building them on that path.
The Central-only confirmation polls already use the same predicate, so they stay
consistent with it.

Simulated the branch: dual publish on with Central healthy publishes; a false
negative rescued by the confirm poll publishes; Central genuinely failing stages for
R2 only; and with dual publish off it never publishes to Central.

Also record the invariants this review round established in the R2 README: a version
is only real once marked complete, metadata is written last on purpose, and an editor
is never published to a Central that lacks its core.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Make release completeness per release, not per directory

The per-directory completion marker did not survive contact with the case it was
meant to cover. Everything a tag publishes shares one version, and the plugin
resolves its editors at its own version, so the coupling that matters is across
artifacts: if the core uploads and marks itself complete but the Game Builder
upload then fails, the core directory is complete in exactly the situation where
the release is not. Skipping metadata on that run hid it only until the next tag,
whose regeneration rebuilds from the same bucket listing and would have advertised
the abandoned version.

Replace it with a single release-level marker, written by mark-release-complete.sh
only once the core reactor and all three editors are up. Discovery advertises no
artifact at a version lacking it. Two mechanisms for one invariant is how the gap
appeared, so the per-directory marker is removed rather than layered.

Verified against the live bucket with the reported scenario: tag 1.0 complete, tag
2.0 uploads core only and is never marked, tag 3.0 complete. Regeneration after 3.0
reports "skipping 2.0" and serves probe-core listing 1.0 and 3.0 only -- previously
2.0 would have been advertised here. Re-running 2.0 with its missing editor and
marking it then lists all three, with <release> correctly remaining 3.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Document mark-release-complete.sh in the R2 README run order

The local-run recipe still ended at regen, which would silently produce metadata
listing nothing: an unmarked version is ignored rather than reported as an error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Close the last gaps in the publish chain

Fail when a requested staging tree is missing or empty (P1)

The script skipped it and exited 0. So an editor build that died before staging
reported a successful R2 upload, the workflow's object check could pass against a
POM left by an earlier partial upload, and the release was then marked complete
without that editor -- defeating the completeness marker added a commit ago. A
caller names a specific tree, so its absence means the build that produces it
failed, and that is now an error.

Honour CN1_DUAL_PUBLISH in the core deploy (P2)

The three editors were changed to stage-only when Central is not a valid target,
but the core deploy was not, so with dual publish off it kept creating immutable
Central releases -- exactly what turning the flag off is meant to stop. All four
deploys now derive skipPublishing the same way.

Mitigate metadata checksum consistency (P2)

Cannot be solved: a metadata set is a body plus four checksum objects and object
storage has no multi-object atomic write. Made unlikely and self-healing instead --
uploads retry with backoff, checksums are written before the body so an interrupted
run still serves the previous parseable metadata, the script is idempotent so a
re-run repairs, and the 60s TTL keeps the window short. Both Maven's default
checksumPolicy and the one in our generated poms are `warn`, so a consumer in that
window is warned rather than broken. Documented as a limitation rather than a fix.

Verified: missing and empty staging trees both exit 1; a published
maven-metadata.xml and its .sha1 agree when compared as bytes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Fix two long-standing CN1CSSCLI bugs, and require the R2 confirmation before marking

Copilot flagged these in its review summaries rather than as inline threads, so they
were never surfaced by a reviewThreads query and I missed them for several rounds.
Both are real, and both predate this branch: CN1CSSCLI moved with a zero-line
content diff.

getMergedFile ignored the cn1.cssMergeFile override

    if (System.getProperty("cn1.cssMergeFile") != null) {
        System.getProperty("cn1.cssMergeFile");   // value discarded
    }

The property was read into a bare expression and thrown away, so anyone setting it
silently got the path derived from the input file instead.

contains() only ever detected a direct parent

It recursed as contains(directory2, parent2), which drops directory1 from the
comparison after one level, so contains(/a, /a/b/c) answered false. The
copy-into-itself guard built on it could be walked straight past. Now recurses with
directory1 held fixed.

Both covered by CN1CSSCLILogicTest.

Require the R2 confirmation before marking a release complete

Review argued a partially staged reactor tree could be published. Tested it: with a
mid-reactor compile failure the workflow's path, maven/target/central-staging, is
never created, and the module-level directory that does appear is empty -- and this
round already made both missing and empty an error. So that path is not reachable.

But checking it surfaced a real gap next door: mark-release-complete did not depend
on r2_core_confirm, which is the step that verifies the expected artifacts are
actually on R2 rather than that an upload exited 0. Marking without it would let a
truncated publish become discoverable. Marking now requires it, and metadata
regeneration follows the marker rather than repeating its conditions.

Also make aws_with_retry's unreachable terminal path an explicit raise instead of an
implicit None fall-through.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Treat a cancelled step as a failure, and vary cache busting per run attempt

Require success, not "not failure", before marking a release (P1)

A cancelled or timed-out step reports outcome 'cancelled'. Combined with always()
on the marker, all three `!= 'failure'` editor checks passed, so a job cancelled
mid-upload could still mark the release complete and make a partial publish
discoverable. Every check is now == 'success'.

The final gate had the same flaw in mirror image: it flagged  'failure', so
a cancelled step was reported as fine. It now classifies anything that is neither
success nor a legitimate skip as a failure. Re-simulated across seven outcome
combinations, including a cancelled editor upload and a cancelled marker step;
both now fail the job, and a legitimately skipped metadata step still passes.

Vary the cache-busting key per attempt (P2)

GITHUB_RUN_ID is stable when a failed job is rerun, so a 404 cached during the
first attempt was re-requested under the same key on the retry -- defeating the
fallback in exactly the situation it exists for, if the Cloudflare no-store rule is
missing. All five polls now append GITHUB_RUN_ATTEMPT as well.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Order qualifiers per Maven, and let cn1:update discover R2-only releases

Sort prereleases below their final version

The comparison padded the shorter version, so 7.0.259 sorted below 7.0.259-rc1 and
an RC would have been advertised as <latest>/<release> after the final shipped.
1.0-alpha also sorted above 1.0.1. Comparison now follows Maven's run-out rule: a
qualifier is below the release it qualifies, a further numeric segment is above it.

The claim that this repo already uses rc-style tags is not true -- there are zero
qualifier tags -- so this was latent rather than active. Fixed anyway because
ordering drives both <release> and which versions the retention job trims.

Point cn1:update at R2 first, with Central as fallback

findLatestVersionOnMavenCentral read repo1 exclusively. With CN1_DUAL_PUBLISH off,
new versions exist only on R2, so cn1:update would have kept reporting the last
Central release and could never discover a newer one -- a footgun this PR creates by
introducing the flag, even though the consumer-side migration is phase 2.

Now reads the R2 metadata first and falls back to Central, so it works during dual
publish, after the cut, and for versions released before the move. Overridable with
-Dcn1.metadataUrl.

Document the concurrency limitation rather than paper over it

A single concurrency group serialises releases so metadata regeneration cannot
interleave, but GitHub keeps only one pending member per group: a third tag pushed
during a release replaces the pending one, and the middle tag is silently never
published. Recorded in the R2 README with the operational mitigation (push tags
singly; the release marker is the proof a tag published) and the real fixes
(external FIFO dispatch, or a reconciliation job) -- none of which belong in this
change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Add the copyright header to UpdateCodenameOneMojo

It carried the NetBeans placeholder rather than a real header, and the gate only
inspects changed files, so modifying it is what surfaced that. Third time this has
come up in this branch; verified locally before pushing this time rather than after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Do not offer a version the project cannot resolve

Repointing cn1:update discovery at the Codename One repository moved discovery
ahead of resolution. Generated projects still ship empty <repositories> and
<pluginRepositories> -- that is phase 2 -- so cn1:update could select a version
that exists only on R2, write it into cn1.version and cn1.plugin.version, and
leave the next build unable to fetch it. Closing one footgun opened another.

Discovery is now a function of what the project can actually reach: the Codename
One repository is consulted only when the pom declares it for both dependencies
and plugins, since the framework resolves from <repositories> and the plugin
itself from <pluginRepositories>, and half the configuration is unusable. A
project without it is offered only what Central can serve, and told why.

This corrects itself rather than needing to be revisited: when phase 2 adds the
repository to generated projects, they start discovering from it automatically.

The predicate is extracted as a pure static so it is testable without a
half-initialised MavenProject, and covered four ways: neither repository kind
declares it, both do, and each one alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Seed the frozen artifacts into R2, and rescue false-negative editor deploys

Seed frozen artifacts (P1)

Excluding sqlite-jdbc from publication while pinning consumers to 7.0.263 left that
coordinate reachable only from Maven Central. That is not history that can be left
behind: codenameone-javase declares it, and codenameone-javase is itself a runtime
dependency of codenameone-maven-plugin, so ordinary plugin resolution reaches it. An
R2 consumer would fail to resolve a release exactly when Central is throttled, which
is the failure this migration exists to survive. The same applies to the frozen
designer and javase-svg for cn1:designer.

seed-frozen-artifacts.sh copies them from Central once, verifying every file against
its .sha1 before upload so a truncated copy cannot be published under an
immutability guard that would then refuse to repair it. Verified end to end against
the live bucket: 24 files seeded for sqlite-jdbc:7.0.263 and both the pom and jar
resolve over repo.codenameone.com.

They deliberately carry no release-completion marker, because they are resolved by
exact pinned version rather than discovered, and a release marker would claim a whole
release is present when only one artifact is. regen-maven-metadata.py knows them as
frozen: it advertises them from what is present, and no longer reports them as
incomplete releases on every run.

Rescue false-negative editor deployments (P2)

The core deploy has a confirmation poll to survive central-publishing reporting
failure for a bundle it accepted; the editors did not, so that false negative left
the release permanently red, and a rerun could not repair it because Central rejects
the already-published immutable version.

The editor confirmations now have ids, run only when their deploy reported failure,
and fail when they do not observe the artifact rather than merely logging. The gate
accepts a clean deploy or a confirmation that saw it. Simulated: a false negative
rescued by the poll passes, both failing fails, and a cancelled R2 upload still fails.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Derive the frozen pins instead of repeating them

maven/pom.xml claims the designer pin "lives in ONE place", and until the seeding
script that claim was true. seed-frozen-artifacts.sh then hardcoded 7.0.263 three
more times, so bumping either pin would have moved the version consumers resolve
while leaving the seeding on the old one -- silently, with no build failure, and
discovered only when someone could not resolve an artifact after cutover.

The script now reads the sqlite pin from <cn1.sqlite.jdbc.version> in maven/pom.xml
and the designer pin from the cn1.designer.version @parameter default in
AbstractCN1Mojo, and exits non-zero naming the file if either cannot be found, so a
rename surfaces immediately rather than seeding the wrong version. Verified both
derive 7.0.263 today, and that renaming the property produces the error rather than
an empty version.

The comment in maven/pom.xml now says the script reads from there, so the claim and
the code agree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Give the editor confirmations the full propagation window they now gate on

Making the editor confirmation polls gating left them at 15 x 20s = 5 minutes,
while the workflow's own comment two lines above said propagation to repo1 can
take 30+ minutes and the core poll already used 90 x 20s. While they were
informational that mismatch was harmless; once their verdict decided the release,
an accepted-but-slow bundle was reported as a failed Central publication -- and a
rerun could not repair it, because Central rejects the already-published immutable
version. That is the same unrecoverable state the rescue was added to prevent.

All three editor loops now cover 30 minutes, matching the core, with the progress
counters and the timeout message updated to match.

The comment above them still described the step as "informational only" and a
"best-effort sanity poll, not a gate", which stopped being true when I made it one.
Replaced with what the step actually does and why the window has to be this wide.

Bounded the job at 240 minutes. Each poll fires only when its own deploy failed, so
the worst case is ~2h of polling on top of the build; that is well inside the 6h
default, but a wedged release should fail within a shift rather than overnight.

Co-Authored-By: 
8000
Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

7.0.263

Toggle 7.0.263's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
JavaScript port: emit a bundle that is ready to deploy as-is (#5466)

* Keep the port's demo fixtures out of every app's JavaScript bundle

JavaScriptPort.jar carries webapp/ so off-repo builds can find port.js, and
the translator copies webapp/assets into every app it builds. That meant
~3.6MB of the port test app's own fixtures -- leather.res (2.1MB),
chrome.res (778KB), video.mp4 (666KB), Handlee-Regular.ttf, Page.html,
test.json -- landed in every JavaScript app's public web root. None of them
is referenced by any class in the port or in CodenameOne core.

Exclude them from the jar. They stay in the source tree, so in-repo builds
and the javascript-screenshots suite still see them. The system themes
HTML5Implementation.getNativeTheme() resolves (iOS7Theme.res,
iPhoneTheme.res, iOSModernTheme.res, android_holo_light.res,
tzone_theme.res) and CN1Resource.res are kept.

Also delete the jar before rebuilding it: Ant's jar task treats an existing
archive as up to date when it is newer than every input file, so on an
incremental build a change to this include/exclude list is silently ignored
and a stale jar ships. That cost a full debugging cycle here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Also exclude the port's own theme.res and the unused tzone theme

assets/theme.res is the port test app's theme (1.4MB). Real apps ship their
own theme.res at the bundle root and getResourceAsStream() deliberately
prefers the root copy for theme.res / CN1Resource.res, so this one is never
read -- confirmed against a running app, whose request log shows the root
theme.res fetched and this one never touched. Shipping it also kept a
shadowing hazard alive for no benefit.

assets/tzone_theme.res is returned by no code path: getNativeTheme() only
ever yields iOS7Theme / iPhoneTheme / iOSModernTheme / android_holo_light /
AndroidMaterialTheme / androidTheme. The single mention of tzone_theme in
the port is inside a comment, which is exactly why it survived the first
pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Correct the theme rationale in the exclusion comment

I had described iOS7Theme/iPhoneTheme/android_holo_light as "the themes
getNativeTheme() resolves" and listed iOSModernTheme among them. That
misread installNativeTheme(): the JS port defaults to the LEGACY pair
(android_holo_light on an Android user agent, iOS7Theme elsewhere) so
existing screenshot baselines stay comparable, and the modern themes are
opt-in via ios.themeMode / and.themeMode / nativeTheme /
javascript.native.theme. The modern .res files live in Themes/ and are
staged per target, not from webapp/assets.

No functional change: the same files are excluded and the same ones kept.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Ship the native themes from Themes/, so opting into a theme mode works

HTML5Implementation.resolveNativeThemeResource() can return six themes --
iOS7Theme, iPhoneTheme, iOSModernTheme, androidTheme, android_holo_light,
AndroidMaterialTheme -- but the bundle only carried three of them, so
setting ios.themeMode / and.themeMode / nativeTheme /
javascript.native.theme to anything modern (or to legacy on Android) hit
the catch in installNativeTheme() and silently fell back to the legacy
theme. The modern themes postdate the old TeaVM port; the ParparVM port
never grew the wiring the other targets have.

The build cannot pick one of the six: resolveNativeThemeResource() decides
at RUNTIME from the browser user agent, so "modern" means iOSModernTheme
in an iOS-like browser and AndroidMaterialTheme everywhere else. Ship the
whole set instead.

Take them from Themes/, the single source of truth kept in sync from the
CSS sources by native-themes-sync.yml, the same way maven/ios folds
iOSModernTheme.res into nativeios.jar and maven/javase copies the set onto
its classpath -- rather than from the port's own copies under
webapp/assets, which drift: the webapp's iPhoneTheme.res was already a
stale version of the Themes/ one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Emit a JavaScript bundle that is ready to deploy as-is

The bundle is meant to be unpacked straight into a web root, but two things
made every consumer post-process it first.

Zip the dist FLAT. zipDirectory() was passed the dist directory's own name as
the zip root, so everything landed under "<MainClass>-js/" and had to be
flattened before it could be served. The old cloud/TeaVM bundle was flat, so
this also lets one deployment script handle both.

Stop copying build descriptors into the bundle. ByteCodeTranslator copies
every non-class input file into the output by basename, so a Maven-built app
leaks META-INF/maven/**/pom.xml -- dependency list and all -- plus
pom.properties and MANIFEST.MF. Harmless for the iOS target, where the output
is a source tree; for the JavaScript target that output IS a public document
root. None of them is an application resource.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Stop bundling the media stack and the developer diagnostics

Three artifacts shipped in every JavaScript app that should not have.

video.js + RecordRTC (~944KB) were already meant to be opt-in. VideoJS is
only ever reached from HTML5Implementation.captureVideo(), which calls
VideoJS.init() behind a try/catch and, when it fails, logs "VideoJS is not
loaded, using default captureVideo behaviour.  Add the
javascript.includeVideoJS build hint..." and falls back. The libraries are
lazy-loaded at runtime by ScriptTool. So the hint documented an opt-in that
nothing on the build side implemented: every app paid for the library
whether or not it captured video. Honour the hint.

samplerate.min.js (~485KB) is unconditional dead weight. Nothing loads it by
path; js/fontmetrics.js only probes for an already-defined Samplerate global
("use libsamplerate if it is available") and nothing ever defines one.

vm_protocol.md and jso-bridge-dispatch-ids.txt are developer artifacts, and
the translator's output directory becomes the app's PUBLIC web root.
vm_protocol.md documents the worker boundary and is checked in under
vm/ByteCodeTranslator/src/javascript/, so copying it into every deployed app
published documentation nobody reads from there.
jso-bridge-dispatch-ids.txt is a sidecar for the dispatch-id mangler, which
is opt-in and off by default (-Dparparvm.js.manglesigs=1); the class walk
that builds it is what the mangler needs, and nothing reads the emitted file
at all. Both now require -Dparparvm.js.diagnostics.

Note reachability cannot decide the media case: HTML5Implementation
references the media classes itself, so they survive culling even in an app
that plays and records nothing (HTML5MediaRecorder still had 129 references
in a console bundle). The build hint is the right gate.

The integration test opts into diagnostics so it still covers
vm_protocol.md; the core-slice test drops that assertion, since
translated_app.js already proves the translator ran.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Address review feedback on the bundle hygiene changes

- isBuildMetadata() is path-aware: it only skips pom.xml /
  pom.properties / MANIFEST.MF that sit under a META-INF directory,
  so an app resource that happens to carry one of those names is
  still copied into the bundle.
- Move the helper above copy()'s Javadoc; inserting it in between
  had detached the @PARAM i / @PARAM o lines from the method.
- emitDiagnostics() parses parparvm.js.diagnostics for 1/true rather
  than testing for presence, matching the documented contract.
- Report a failed prune instead of silently shipping the file: both
  js/videojs and js/samplerate.min.js verify the deletion happened
  and log a warning if it did not.
- Add the missing licence headers to JavascriptBundleWriter and
  JavascriptCn1CoreCompletenessTest (check-copyright-headers).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

7.0.262

Toggle 7.0.262's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix asymmetric border radius rendering in RTL (#5456)

* Fix RTL asymmetric border radii

* Fix RTL border review feedback

7.0.261

Toggle 7.0.261's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix GC storm on retained large-array loads (issue 5425, final Dtest s…

…hape) (#5446)

* Fix GC storm on retained large-array loads (issue 5425 final Dtest shape)

Arrays above CN1_BIBOP_MAX_OBJECT always take the legacy allocation
path, whose only byte-based GC signal was the pre-BiBOP 1MB
isHighFrequencyGC re-arm -- 24x more aggressive than the BiBOP trigger.
Retaining a few hundred 10K byte[] blocks (no garbage at all) kept full
collection cycles starting every 200ms wait interval over the whole
survivor set: "allocating the large arrays is triggering a global
multiple times".

Fix, all gated on the BiBOP build so -DCN1_DISABLE_BIBOP keeps the old
shape verbatim:
- cn1LegacyBytesSinceGc: per-cycle legacy allocation byte counter,
  reset in cn1BibopBeginGcCycle alongside bibopBytesSinceGc.
- Event-driven trigger in codenameOneGcMalloc's legacy tail mirroring
  cn1BibopMaybeGc: async System.gc() when legacy volume crosses
  CN1_LEGACY_GC_TRIGGER_BYTES (24MB, -D overridable), so legacy-churn
  workloads still collect promptly.
- isHighFrequencyGC re-arms at max(1MB, bibopGcTriggerBytes): with both
  paths event-driven on volume, the 200ms loop only needs to re-arm
  when the volume budget would have been crossed anyway.
- CN1_GC_LOG_CYCLES env-gated cycle tracer in codenameOneGCMark (one
  stderr line per cycle) for CI assertions and on-device diagnosis.

Guard: com.bench.LargeArrayLoad models the final Dtest (persistent
small survivor set; retained large-array pass producing no garbage;
wall-stretched phases so re-arm windows are machine-independent), and
LargeArrayGcIntegrationTest gates on cycle count via the tracer --
measured 15 cycles unfixed vs 6 fixed, stable across runs, budget 10 --
plus RESULT parity with the host JVM. Cycle count is load-independent,
unlike phase wall times which only inflate under mutator contention.

Validated: integration test passes fixed / fails unfixed; gauntlet
green (tortures byte-identical to host JVM, GcStress/MtStress in both
cooperative and CN1_GC_SIGNAL_STOP modes).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Add copyright headers to large-array GC guard test files

check-copyright-headers flagged the two new test files; use the
standard Codename One GPLv2 + Classpath Exception header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Address review: exchange-reset for legacy byte counter, bracket gate

Use the same atomic_exchange idiom as the BiBOP reset for
cn1LegacyBytesSinceGc (racing adds land before the swap or charge the
next cycle), and mirror cn1BibopMaybeGc's native-allocation-bracket
gate on the legacy trigger: without conservative roots a bracketed
thread must not trigger a cycle; with them, gating would starve the
trigger for native-bracket allocators.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Address review: static legacy byte counter, document tracer gating

cn1LegacyBytesSinceGc is file-local -- make it static. The
CN1_GC_LOG_CYCLES tracer stays outside the CN1_DISABLE_BIBOP guard on
purpose (A/B builds need the same cycle-count observable); document
that instead of gating it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Surface app-side CN1SS diagnostics when the Linux screenshot gate fails

A missing screenshot means a test never emitted its capture; the only
evidence is the app's own CN1SS:WARN/ERR lines in app-output.log, which
the gate job downloads but never printed. Dump the relevant lines on
gate failure so a miss like the current ToastBarTopPosition one is
triageable from the job log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Address review: edge-trigger legacy GC schedule, stable segfault-retry marker

Level-triggering called System.gc() (lock+notify) on every legacy
allocation between the 24MB crossing and the counter reset; trigger on
the crossing only, and drop the gcCurrentlyRunning suppression so a
crossing during a running cycle schedules the follow-up pass instead of
losing the edge. The integration test's segfault retry now keys on a
controlled VM_RUN_EXIT marker instead of JUnit's assertion-message
format.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Add poll-state tracing to ToastBarTopPosition for the Linux CI miss

The Linux GTK suite times this test out with the screenshot never
emitted (stage=capture-requested, both arches, reproducible). The
per-second probes and reset-event prints show which wait the
choreography dies in; the CN1SS:WARN prefix routes them into the
gate-failure diagnostic dump automatically.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Add copyright header to ToastBarTopPositionScreenshotTest

Touching the file pulled it into check-copyright-headers scope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Fix Thread.sleep truncation by signals (Linux ToastBar CI failure root cause)

usleep()/nanosleep() return EINTR when any signal lands and POSIX never
restarts them (SA_RESTART explicitly excludes them). The conservative-
roots collector signal-stops SLEEPING threads every cycle to scan their
native stacks, so ParparVM's single-usleep Thread.sleep slept only
until the next collection or any other process signal: measured
Thread.sleep(3000) returning after ~20ms on the Linux port under
allocation churn. java.util.Timer schedules through Thread.sleep, so
every TimerTask fired almost immediately -- ToastBar's 10-minute
"practically unexpiring" status dismissed itself mid-test, which is
exactly the ToastBarTopPosition miss on both Linux arches (the toast
kept vanishing between the test's polls, so its re-show loop never
reached a stable capture).

Sleep on a monotonic deadline (wall clock on the MSVC target, matching
nanoTime) and resume across early wakeups, chunked below 1s because
POSIX allows usleep(>=1e6) to fail with EINVAL (musl). The loop honors
the interrupt flag, so Thread.interrupt() now shortens a sleep
deliberately instead of relying on an accidental EINTR.

Validated on the translated Linux port under Xvfb: Thread.sleep(3000)
now sleeps 3000ms exactly (was ~20ms) and the ToastBar show/poll
choreography runs to completion; full gauntlet green (10 tortures
byte-identical to host JVM, GcStress x10 + MtStress x6 in both stop
modes, LargeArrayGc cycle gate unchanged at 6).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Address review: reserved identifier, atomic tracer gate, temp cleanup, probe gating

Rename __prevLegacyBytes (double underscore is implementation-reserved),
make the CN1_GC_LOG_CYCLES lazy gate an atomic, delete the integration
test's temp trees in a finally (the translated CMake build is large),
and emit ToastBar diagnostics only during extended waits so healthy
runs stay quiet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Add TimerLatency sleep/timer fidelity probe

Guards the Thread.sleep signal-truncation defect: signed timer-fire
deviations (the broken behavior is EARLY fire, which a lateness-only
metric clamps away) plus a direct sleep-duration check; prints
TIMER_LATENCY_OK / TIMER_LATENCY_DEGRADED. Verified OK on the fixed VM
and the host JVM.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Fix MSVC build: useconds_t is absent on the clang-cl target

The Windows clean-target job failed compiling the new Thread.sleep loop:
cn1_win_compat's usleep shim has no useconds_t typedef. Cast to
JAVA_INT as the pre-existing code did.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Saturate the Thread.sleep deadline instead of overflowing

Thread.sleep(Long.MAX_VALUE) is a real park-this-thread idiom; the new
deadline computation would wrap negative and return immediately. Clamp
delta and deadline to LLONG_MAX. SleepEdge guards the edge cases: tiny
sleeps stay accurate, a Long.MAX_VALUE sleep parks, and interrupt()
wakes it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Preserve historical interrupt semantics in the Thread.sleep resume loop

This VM has never delivered InterruptedException from sleep --
interrupt() only sets the flag -- so the resume loop must not exit
early on it either: waking without the exception would be a third
behavior, neither historical nor Java's. Proper interrupt delivery is
a separate change. SleepEdge now asserts only duration fidelity and
the Long.MAX_VALUE park.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Restore review fixes clobbered by a stale local snapshot restore

A local A/B build step restored cn1_globals.m from an outdated scratchpad
snapshot, silently reverting four already-reviewed changes that then
shipped in 811bce7. This re-applies them:

- cn1LegacyBytesSinceGc is static again (single-TU internal linkage)
- cn1BibopBeginGcCycle resets it with the same atomic_exchange idiom as
  the BiBOP counter (a racing fetch_add is never dropped)
- the legacy trigger is edge-triggered on the 24MB crossing instead of
  level-triggered (at most one System.gc() schedule per cycle window,
  no !gcCurrentlyRunning suppression that would lose the edge)
- the native-allocation-mode gate mirrors cn1BibopMaybeGc exactly,
  including the CN1_CONSERVATIVE_GC_ROOTS carve-out

Also: README wording fix for the TimerLatency gate and a design note in
TimerLatency.java documenting the deliberate one-Timer-per-task shape
(ParparVM's Timer spawns a thread per schedule()).

Revalidated locally: LargeArrayLoad 6 GC cycles (x2 runs), RESULT=1099
host parity, TIMER_LATENCY_OK, SLEEP_EDGE_OK, MapTorture MATCH,
GcStress/MtStress clean in both stop modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Harden legacy GC counter width and sleep parking for the Windows target

Two review findings on the Windows (LLP64 / no-signal-stop) target:

- cn1LegacyBytesSinceGc was _Atomic long, which is 32-bit under LLP64;
  the counter accumulates raw bytes between cycle resets, so it now uses
  long long (with the fetch_add operand and prevLegacyBytes widened to
  match) so the edge-trigger comparison can never see a wrapped value.

- Thread.sleep published threadActive=FALSE without a cooperative park
  capture (a pre-existing pattern this PR's rewrite kept). Under
  CN1_CONSERVATIVE_GC_ROOTS the collector scans a parked thread's native
  stack either via the capture or a signal stop, and Windows has no
  signal-stop fallback -- an uncaptured sleeper's native-stack roots
  went unscanned for the cycle. sleep now runs CN1_GC_PARK_CAPTURE
  before parking, per the CN1_YIELD_THREAD contract, and drops the
  capture on resume exactly like CN1_RESUME_THREAD.

Revalidated locally: LargeArrayLoad 6 GC cycles (x2), RESULT=1099 host
parity, SLEEP_EDGE_OK, TIMER_LATENCY_OK (max_early_ms=0,
min_sleep1500_ms=1500), MapTorture byte-identical to the host JVM,
GcStress x4 + MtStress x3 in cooperative and CN1_GC_SIGNAL_STOP=1 modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Make the Windows sleep clock monotonic; add cleanup and drift breadcrumbs

- The sleep deadline on the MSVC/clang-cl target was computed from
  gettimeofday, a wall clock: a system time step mid-sleep stretched or
  cut the remaining duration. cn1_win_compat gains cn1_monotonic_micros
  (QueryPerformanceCounter, overflow-safe split divide) and
  cn1SleepNowMicros now uses it, keeping windows.h out of translated
  compilation units per the compat layer's rule.

- LargeArrayGcIntegrationTest's best-effort temp cleanup no longer fails
  silently: one stderr line per root reports the first failed deletion
  so leaked translated build trees leave a breadcrumb on self-hosted
  runners.

- LargeArrayLoad and its CI twin LargeArrayGcApp now carry explicit
  KEEP IN SYNC notes in both directions, documenting that the shared
  host-JVM-asserted RESULT= value turns one-sided edits into a visible
  parity break rather than silent drift.

Revalidated locally: LargeArrayLoad 6 GC cycles (x2), RESULT=1099,
SLEEP_EDGE_OK, TIMER_LATENCY_OK; vm/tests module test-compiles clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Add the Codename One GPLv2+Classpath header to cn1_win_compat

The copyright gate checks every file a PR touches; these two predate the
gate and entered the diff via the monotonic-clock shim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Drop the lazily-written freq cache in cn1_monotonic_micros

The unsynchronized static was a formal C data race across concurrent
sleepers; QueryPerformanceFrequency is a cheap userspace read of a boot
constant, so just call it per invocation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Latch the legacy GC trigger per cycle; throw on negative Thread.sleep

Two review findings:

- The edge-triggered legacy volume trigger could lose its edge for good:
  a crossing that landed while the allocating thread was inside a
  native-allocation bracket (gated when conservative roots are off) was
  skipped, and every later allocation saw prev already above the
  threshold, deferring collection to the GC thread's 30s idle wake. The
  trigger is now LEVEL-triggered on the counter and LATCHED
  (cn1LegacyGcScheduled, cleared at cycle begin after the counter reset)
  so a suppressed crossing is retried by the next out-of-bracket legacy
  allocation on any thread while System.gc() is still scheduled at most
  once per cycle window.

- Thread.sleep(long) now throws IllegalArgumentException for negative
  millis per the JDK contract (the (millis, nanos) overload already
  did). The guard lives in Java -- sleep(long) delegates to a private
  native sleepImpl -- so the exception class is retained by the
  translator's reachability pass wherever sleep is used. SleepEdge now
  asserts the negative case.

Revalidated locally: LargeArrayLoad 6 GC cycles (x2), RESULT=1099,
SLEEP_EDGE_OK (negativeThrew=true), TIMER_LATENCY_OK, MapTorture
parity, GcStress x3 + MtStress x2 in both stop modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Bind sleepImpl in the JavaScript target runtime and registry

Thread.sleep(long) became a Java wrapper delegating to the native
sleepImpl(long), so the JS translator now emits calls to
cn1_java_lang_Thread_sleepImpl_*; the runtime binds those (keeping the
old sleep aliases for previously-translated bundles), the native
registry lists the new symbol, and the opcode coverage test asserts it.

Also: the Linux gate's diagnostic grep uses -E with standard
alternation instead of GNU basic-regex escapes.

JavascriptOpcodeCoverageTest passes (3/3); LargeArrayGcIntegrationTest
re-verified green at both e2a6c74 and 0a13eca on this machine (the
one local failure was CPU-starvation flake from a concurrent full
module build, not a code regression).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

* Add the GPLv2+Classpath header to JavascriptOpcodeCoverageTest

The copyright gate checks every PR-touched file; this pre-existing test
entered the diff via the sleepImpl binding assertion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0133QDXUjHwX9uprkupEgB29

---------

Co-authored-by: Claude <noreply@anthropic.com>

7.0.260

Toggle 7.0.260's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Deploy publisher-triggered port status updates (#5395)

7.0.259

Toggle 7.0.259's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix tagged static final heap removal crash (#5371)

7.0.258

Toggle 7.0.258's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix release CI: bump certificatewizard poms to release version (#5355)

The release-on-maven-central workflow's "Deploy Signing Wizard editor"
step failed with 403 Forbidden because update-version.sh never bumped
the scripts/certificatewizard poms off 8.0-SNAPSHOT. mvn deploy therefore
targeted the Central *snapshots* repository, which the release job has no
rights to publish to.

The Game Builder editor (scripts/gamebuilder) — same out-of-reactor,
own-coordinates shape — was already handled; certificatewizard was added
later without the matching wiring. Mirror the gamebuilder block: rewrite
the three module versions plus the cn1.version/cn1.plugin.version the
editor builds against, and git add the tree before committing.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0