Check build hints at compile time instead of shipping them inert - #5586

Merged
shai-almog merged 244 commits into
masterfrom
build-hint-annotations
Aug 27, 2026
Merged

Check build hints at compile time instead of shipping them inert#5586
shai-almog merged 244 commits into
masterfrom
build-hint-annotations

Conversation

@shai-almog

@shai-almogshai-almog commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

The problem

A build hint is a codename1.arg.<name>=<value> line that reaches a builder as request.getArg(name, default). Nothing checked the name, so a misspelling was accepted, copied into the build request, never read, and silently discarded — a green build with the setting simply not applied.

Our own agent reference had been shipping keys in exactly that state:

Documented in skill/references/build-hints.mdActually read by the builders
android.xPermissionsandroid.xpermissions (AndroidGradleBuilder.java:1206)
android.minSdkVersionandroid.min_sdk_version
android.sdkVersion(nothing — android.targetSDKVersion is the real one)

The change

87 hints can now be written as annotations on the application's main class, where javac does the checking: a misspelled name is an unknown symbol, a wrong value type is a type error, and a value outside a hint's supported set is an unknown enum constant.

@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)
@Build(nativeTheme = ThemeMode.MODERN)
@DesktopBuild(titleBar = DesktopTitleBar.NATIVE, width = 1280, height = 800)
@Ios(themeMode = ThemeMode.MODERN, newStorageLocation = Toggle.ON)
publicclassMyApplicationextendsLifecycle {
}

Seven annotations — @Ios, @Android, @DesktopBuild, @Build, @Hardening, @IosPrivacy, @OnDeviceDebug — and ten enums.

No attribute has a default that means anything

Every default is a marker for "nothing was said": Toggle.DEFAULT, "", {}, 0, or an enum's @HintUnset constant. An attribute left out is absent from the class file, so the processor emits nothing for it and the build decides, exactly as it does for a hint nobody wrote.

This is the reason there are no boolean attributes left. boolean appBundle() default false reads as "off unless you turn it on" while AndroidGradleBuilder defaults android.appBundle to true — and a copy of the server's answer compiled into every app already built cannot follow the server when it changes. Toggle is the three-state replacement, and a test refuses any hint attribute that declares a value-bearing default.

The builders are untouched.BuildHintAnnotationProcessor converts the annotations back into the same key/value pairs, and CN1BuildMojo merges them before the command-line overlay, the CN1Lib merges and both preflights — so a library still appends onto an annotation-supplied value and -D still wins. Simulator publishes them as system properties at startup so cn1:run sees hints that no longer live in the properties file.

The properties file is untouched too. It stays the way to set the long tail and the open-ended families such as android.permission.<NAME> that an annotation cannot express, with no new warnings or errors on that path. Declaring one hint both ways is a build error.

Where a hint is declared

Exactly one of two places:

  • CodenameOne/src/com/codename1/annotations/buildhints if it has an annotation. These are hand-written and are the source of truth for the hints they expose; BuildHintAnnotationReader reads them back with ASM rather than any file restating them.
  • maven/build-hint-catalog otherwise — dynamic families, build-service-only hints, the long tail.

The hint set used to be described in five places that had drifted apart: a prose table in the developer guide, a runtime scraper of that table in the Settings tool that guessed each type by string-matching the description, a fifteen-entry schema in the simulator, a fourteen-entry separator map in the plugin, and a hand-written agent reference. Only 147 of ~520 names appeared in more than one.

Nothing is generated into the tree

No generated file is committed. BuildHintCodeGenerator renders two views, both during a build:

  • cn1-build-hints.json for the two editors that are Codename One apps and so have no bytecode reader — the Settings tool and the simulator's hint editor. Each module that needs it renders it into its own target/classes (maven/javase, maven/codenameone-maven-plugin, scripts/settings/common). The catalog cannot render its own, because the generator lives in build-hint-tools, which depends on the catalog.
  • the developer guide's table, rendered when the guide is built.

Anything that can read bytecode reads the annotations directly and never touches the data file.

The guide's table goes from 208 rows to 570 with no prose lost, and gains Type, Default and Annotation columns it never had. An annotated hint's Default reads "set by the build" rather than a value, which is the honest statement of the paragraph above.

Enums are emitted only where the accepted set is demonstrable from the code that reads the hint — HardeningPreflight rejects an unknown harden.level, IOSDependencyManager throws on an unknown ios.dependencyManager, and GenerateDesktopAppWrapperMojo silently falls back to native on an unknown desktop.titleBar, which is precisely the failure this removes.

Scope: generated projects are deliberately not migrated here

Every project the archetype and the initializr produce is pinned to a released Codename One version — the initializr hardcodes 7.0.267 in GeneratorModel.CN1_PLUGIN_VERSION — and no released core carries com.codename1.annotations.buildhints. A generated project would import annotations that do not resolve and fail to compile before the user has written a line.

So the archetype's __mainName__.java and codenameone_settings.properties, and the initializr's common.zip and four source archives, are unchanged. They move to annotations in a follow-up once a release containing the package is out. scripts/skindesigner (7.0.255) stays on properties for the same reason, and cn1:migrate-build-hints refuses any project whose core lacks the package.

The in-repo tool projects that build against the snapshot from source are migrated: gamebuilder, video-builder, cn1playground, certificatewizard, guibuilder, fidelity-app, purchase-test-app, settings, hellocodenameone. protocol-e2e is not in that list: its only hint is codename1.arg.java.version, which has to stay in the properties file because the compiler needs it in order to compile the class that would otherwise declare it.

docs/demos is deliberately excluded: it is the developer guide's snippet project, full of intentionally incomplete fragments, and running the annotation processors over it fails by design.

Settings tool

It no longer scrapes the guide's AsciiDoc and guesses types; it reads the catalog. It also validates closed value domains, and refuses to edit a hint an annotation already owns — reading META-INF/codenameone/build-hints.properties and showing "Set by @Ios(themeMode) on the main class" — because writing a property for such a hint would create the duplicate declaration that fails the next build.

Its own tests had never run anywhere: both workflows that touched the module passed -Dmaven.test.skip=true, so every test written for the POM reader and the hint editor was dead weight. They now run in PR CI, 161 of them.

Gates

  • scripts/check-build-hint-catalog.sh fails when code reads a hint the catalog does not describe, and when our own docs or project templates name one that no builder reads. Its baseline is empty, so it is a hard gate rather than a ratchet. 497 hints read by the builders, all described.
  • scripts/gen-build-hint-annotations.sh --check asserts the render still succeeds and produces the full set. It is not a drift check — there is no committed copy to drift from — but a render that silently produced nothing would otherwise reach a user as an editor with no hints in it.
  • A JUnit suite checks the catalog's own consistency (attribute-name legality including the JLS 9.6.1 Object/Annotation method-name rule, enum domains, separators matching what LibraryHintMerger used to define), and build-hint-tools asserts the invariants that need the complete hint set, since that only exists on a classpath carrying the rendered data.
  • BytecodeComplianceMojo re-stamps the manifest after its in-place class rewrites, so the order of process-annotations against it stops mattering.
  • The developer-guide and website workflows list every input the table is rendered from — the annotations, the catalog, the renderer and the script — so a reworded attribute cannot change published documentation without running the AsciiDoc and Vale checks.

A build hint is a `codename1.arg.<name>=<value>` line that reaches a builder as
`request.getArg(name, default)`. Nothing checked the name, so a misspelling was
accepted, copied into the build request, never read, and silently discarded: a
green build with the setting simply not applied.
Our own agent reference had been shipping `android.xPermissions`,
`android.minSdkVersion` and `android.sdkVersion` for exactly that reason. The
builders read `android.xpermissions`, `android.min_sdk_version`, and nothing at
all.
Most hints can now be written as annotations on the application's main class,
where javac does the checking: a misspelled name is an unknown symbol, a wrong
value type is a type error, and a value outside a hint's supported set is an
unknown enum constant.
@iOS(newStorageLocation = true, themeMode = IosThemeMode.MODERN)
@android(minSdkVersion = 24, useAndroidX = true)
@desktop(titleBar = DesktopTitleBar.NATIVE)
public class MyApplication extends Lifecycle {
}
The builders are untouched: `BuildHintAnnotationProcessor` converts the
annotations back into the same key/value pairs and `CN1BuildMojo` merges them
before the command-line overlay, the CN1Lib merges and both preflights, so a
library still appends onto an annotation-supplied value and `-D` still wins.
`Simulator` publishes them as system properties at startup so `cn1:run` sees
hints that no longer live in the properties file.
The properties file is untouched too. It stays the way to set the long tail and
the open-ended families such as `android.permission.<NAME>` that an annotation
cannot express, with no new warnings or errors. Declaring one hint both ways is
a build error.
One catalog, five generated views
---------------------------------
The hint set had been described in five places that had drifted apart: a prose
table in the developer guide, a runtime scraper of that table in the Settings
tool that guessed each type by string-matching the description, a fifteen-entry
schema in the simulator, a fourteen-entry separator map in the plugin, and a
hand-written agent reference. Only 147 of ~520 names appeared in more than one.
`maven/build-hint-catalog` is now the single source of truth (529 hints: 457
mined from the builders, 56 documented-but-unread, 16 dynamic families; 82
exposed as annotation attributes). The annotations, the binding table the
processor reads back, the guide's table, the simulator's editor schema and the
agent reference are all generated from it. The guide's table goes from 208 rows
to 529 with no prose lost.
Enums are emitted only where the accepted set is demonstrable from the code that
reads the hint -- `HardeningPreflight` rejects an unknown `harden.level`,
`IOSDependencyManager` throws on an unknown `ios.dependencyManager`, and
`GenerateDesktopAppWrapperMojo` silently falls back to `native` on an unknown
`desktop.titleBar`, which is the failure this removes.
Generated projects
------------------
The archetype and all four initializr templates now carry the annotations, and
`cn1:migrate-build-hints` moves an existing project over. Eleven in-repo
projects are migrated. `java.version` deliberately stays in the properties file:
it picks the toolchain that compiles the class the annotations live on.
Gates
-----
`scripts/check-build-hint-catalog.sh` fails when code reads a hint the catalog
does not describe, and when our own docs or templates name one that no builder
reads. Its baseline is empty, so it is a hard gate rather than a ratchet.
`scripts/gen-build-hint-annotations.sh --check` fails on generated-file drift.
Both run in the Java 8 leg of PR CI.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:8d2cfcfde3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadmaven/pom.xml
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs[Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • build-hint-catalog: 0 findings (no issues)
    • build-hint-tools: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

`.gitignore` carries a repo-wide `**/build/*`. The catalog's package is
`com.codename1.build.shared`, so all 13 of its sources sat under a path segment
named `build` and `git add` silently skipped them. Only `pom.xml` was committed:
the module built locally from the working tree and produced an empty jar in CI,
which is why `codenameone-maven-plugin` then failed with `cannot find symbol` on
`BuildHints` and nearly every job went red.
The sibling `platform-feature-catalog` lives in the same package and is fine,
because it was added before that rule existed -- tracked files stay tracked, so
nothing ever pointed at the hazard.
Un-ignore `build` when it is a Java package rather than a build output
directory, with the rationale beside the rule so the next file added there is
not lost the same way. `maven/core/build/*` and `CodenameOne/build/*` stay
ignored.
Also from review:
- Every bare `open()` in the four Python scripts now uses a context manager, so
the handle closes even if parsing or `json.dump` raises, and the writes state
their encoding.
- The generator no longer emits an IP literal as an annotation default. PMD
reads `default "127.0.0.1"` as hardcoded configuration, and the default clause
is documentation only -- the processor emits a hint solely for members the
developer actually wrote -- so the value moves to the javadoc where it belongs.
- Files the migration touched that never carried a copyright header now have the
complete one. The archetype's `__mainName__.java` is excluded instead: it is a
template for the user's own application class, and stamping a Codename One GPL
header onto it would put our licence on their code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Pin generated projects to a version containing the annotations

The updated Initializr starter archives now import and use com.codename1.annotations.buildhints, but GeneratorModel.java:44 still generates projects pinned to CN1 7.0.267, whose core artifact predates this package. Consequently every newly generated barebones, Kotlin, Grub, or Tweet project fails compilation on the unresolved annotations unless the user manually changes the CN1 version; either defer these template changes or update the generated runtime/plugin version to the first release containing them.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The archived bootstrap ran generation at module scope, so gen_external.py's
`import gen_catalog` -- which only wants three helper functions -- rewrote every
catalog source as a side effect. Generation and its diagnostics now live in
`main()` behind a `__main__` guard, and the module-level file reads became
`load_license()` / `load_mined()` / `load_docs()`, so importing does no I/O and
cannot fail on inputs the archived copy deliberately does not carry.
Verified both directions: importing leaves the catalog untouched, and running
the two scripts end to end still reproduces the committed catalog byte for byte.
Also drops `json` and `subprocess` from check-build-hint-catalog.py. Both were
left from an earlier version that shelled out to the miner instead of importing
it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:d727c7d976

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadscripts/gen-build-hint-annotations.sh Outdated
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendscalar fallback (no native SIMD)
SIMD int-add (64K x300)java 196ms / native 73ms = 2.6x speedup
SIMD float-mul (64K x300)java 144ms / native 89ms = 1.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode83.000 ms
Base64 CN1 decode86.000 ms
Base64 native encode391.000 ms
Base64 encode ratio (CN1/native)0.212x (78.8% faster)
Base64 native decode310.000 ms
Base64 decode ratio (CN1/native)0.277x (72.3% faster)
Image encode benchmark statusskipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 60ms / native 4ms = 15.0x speedup
SIMD float-mul (64K x300)java 80ms / native 3ms = 26.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode190.000 ms
Base64 CN1 decode381.000 ms
Base64 SIMD encode99.000 ms
Base64 encode ratio (SIMD/CN1)0.521x (47.9% faster)
Base64 SIMD decode91.000 ms
Base64 decode ratio (SIMD/CN1)0.239x (76.1% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)22.000 ms
Image createMask (SIMD on)15.000 ms
Image createMask ratio (SIMD on/off)0.682x (31.8% faster)
Image applyMask (SIMD off)71.000 ms
Image applyMask (SIMD on)35.000 ms
Image applyMask ratio (SIMD on/off)0.493x (50.7% faster)
Image modifyAlpha (SIMD off)44.000 ms
Image modifyAlpha (SIMD on)22.000 ms
Image modifyAlpha ratio (SIMD on/off)0.500x (50.0% faster)
Image modifyAlpha removeColor (SIMD off)34.000 ms
Image modifyAlpha removeColor (SIMD on)25.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.735x (26.5% faster)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 63ms / native 5ms = 12.6x speedup
SIMD float-mul (64K x300)java 63ms / native 4ms = 15.7x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode327.000 ms
Base64 CN1 decode216.000 ms
Base64 SIMD encode176.000 ms
Base64 encode ratio (SIMD/CN1)0.538x (46.2% faster)
Base64 SIMD decode132.000 ms
Base64 decode ratio (SIMD/CN1)0.611x (38.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)28.000 ms
Image createMask (SIMD on)23.000 ms
Image createMask ratio (SIMD on/off)0.821x (17.9% faster)
Image applyMask (SIMD off)58.000 ms
Image applyMask (SIMD on)56.000 ms
Image applyMask ratio (SIMD on/off)0.966x (3.4% faster)
Image modifyAlpha (SIMD off)66.000 ms
Image modifyAlpha (SIMD on)28.000 ms
Image modifyAlpha ratio (SIMD on/off)0.424x (57.6% faster)
Image modifyAlpha removeColor (SIMD off)44.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.727x (27.3% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 55ms / native 3ms = 18.3x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode268.000 ms
Base64 CN1 decode152.000 ms
Base64 SIMD encode66.000 ms
Base64 encode ratio (SIMD/CN1)0.246x (75.4% faster)
Base64 SIMD decode64.000 ms
Base64 decode ratio (SIMD/CN1)0.421x (57.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)13.000 ms
Image createMask (SIMD on)9.000 ms
Image createMask ratio (SIMD on/off)0.692x (30.8% faster)
Image applyMask (SIMD off)24.000 ms
Image applyMask (SIMD on)20.000 ms
Image applyMask ratio (SIMD on/off)0.833x (16.7% faster)
Image modifyAlpha (SIMD off)17.000 ms
Image modifyAlpha (SIMD on)12.000 ms
Image modifyAlpha ratio (SIMD on/off)0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off)21.000 ms
Image modifyAlpha removeColor (SIMD on)13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.619x (38.1% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

Three separate gates rejected generated output. Each is fixed in the generator
so the class of problem cannot come back through a catalog edit.
Unmappable characters. The prose is imported from the developer guide, which
uses typographic punctuation, and `CodenameOne/src` is also compiled by an Ant
javac step with ASCII encoding where a single em dash is
`error: unmappable character for encoding ASCII` -- a build failure, not a
warning. A Unicode escape would not have helped: javac expands `\uXXXX` before
it strips comments, so the character reappears. `toAscii` now folds the
punctuation that actually occurs, and *refuses* anything it has no mapping for
rather than dropping it, because silently deleting a character from a hint's
documentation is the worse outcome.
Broken table. `ios.spm.packages` is documented as `identity|url|requirement`,
and a bare `|` starts a new AsciiDoc cell, so asciidoctor reported "dropping
cells from incomplete row" for the whole 529-row table. Cells are escaped now.
Vale. The guide enforces the Microsoft style as errors, and the generated table
feeds it, so the catalog's prose has to satisfy it too: contractions, no
"and so on", no stray adverbs. A default value is not prose, though -- the one
remaining hit was `android.file_paths`, whose default is an XML fragment -- so
a quoted default now carries the `// vale-skip:` comment .vale.ini documents
for individual false positives.
Also fixes a data bug the guide exposed. The miner preserved Java escape
sequences instead of decoding them, so `android.file_paths` and
`android.facebook_permissions` recorded defaults containing literal
backslashes that the build never sees, and those reached the rendered table.
The miner decodes escapes and re-quotes safely, and the two catalog entries are
corrected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/build_hint_miner.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:96bff9038a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

`killedThreadReportsItselfFinished` failed the Java 21 leg with
"FormTest timed out after 5000ms; edt=initialized pendingSerialCalls=0". The
waits in this class used a 5000ms deadline, which is exactly the `@FormTest`
timeout in EDTTestInterceptor -- so on a loaded runner the poll loop consumed
the entire harness budget and the interceptor fired first. The report then said
only that the method timed out, with nothing about which condition never became
true.
The waits now use 2000ms, well inside the harness budget and still roughly two
thousand times the ~1ms these threads actually take to stop. A genuine
regression now fails on the test's own assertion, which names what went wrong.
Pre-existing (the test arrived with #5526) and unrelated to the build hint work:
core-unittests has no dependency on the JavaSE port, so none of the simulator
registration in this branch runs there, this branch changes nothing under
com.codename1.db or EasyThread, and the Java 8 leg passed the same commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:a343fe3335

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

A mojo's defaultPhase does not bind it to a project -- the project's POM has to
-- and nothing turns a build hint annotation back into a codename1.arg.* pair
except the process-annotations goal. So migrating a project without that
binding deleted working properties and replaced them with annotations no goal
ever reads: the hints vanished from the build with no diagnostic anywhere.
Five projects in this branch were already in that state. gamebuilder,
docs/demos, video-builder and cn1playground bind the plugin but not that goal,
so the binding is added. input-validation-app's common module has no build
section at all, so its migration is reverted rather than inventing a lifecycle
for a demo app.
The goal now checks the reactor for the binding and refuses with the execution
block to paste, so this cannot happen to anyone else.
Three more from the same review:
- The deletion pass recognized only `key=value`. `Properties.load` also accepts
`key:value`, `key value`, escaped separators inside the key, and logical
continuation lines; a declaration it failed to match was left behind while the
annotation was added, so the next build failed with the duplicate-hint error
this goal exists to prevent. Keys are parsed the way Properties.load defines
them now, with a unit test per form.
- The settings file was read as ISO-8859-1 and written back as UTF-8, turning
any unrelated non-ASCII byte -- an accented displayName, say -- into mojibake.
It is written back as ISO-8859-1.
- cn1.androidTheme and cn1.nativeTheme are deprecated aliases of and.themeMode
and nativeTheme, which the builders honour as fallbacks. Neither declared
aliasOf, so conflict detection missed them and one value silently won.
Also: the generation script rebuilt the generator only when its class was
absent, so editing a catalog source and rerunning regenerated every view from
the previous build's bytecode -- reporting success while ignoring the edit, and
passing --check on a tree that was genuinely stale. It always rebuilds now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:0edef42ca4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…plicating a hint
docs/demos is the developer guide's snippet project: deliberately incomplete
code fragments that illustrate @entity, @route, @AppIntent and @mapped. Binding
process-annotations there put those snippets in front of the other processors,
which correctly rejected six of them, so the migration is reverted and its two
hints are back in the properties file. That the project omitted the goal was the
point, not an oversight.
The other three newly bound projects were checked rather than assumed:
gamebuilder, video-builder and cn1playground each run process-annotations
cleanly and emit 6, 3 and 5 hints respectively.
Settings could still create the duplicate the migration is careful to avoid. In
a generated project ios.themeMode and its neighbours are annotations, but the
Build Hints UI decides a hint is inactive from the properties file alone and its
Add button writes a property -- producing a second declaration that fails the
next build. The tool now reads META-INF/codenameone/build-hints.properties, the
file the processor writes on every build and deletes when the last annotation
goes, and renders those hints read-only with the attribute that owns them:
"Set by @iOS(themeMode) on the main class." An unbuilt project has no such file
and behaves as before.
Also fixes the SpotBugs finding this branch introduced: `backslashes % 2 == 1`
in the continuation scan is false for negative odd numbers, so it is `!= 0`.
The count cannot go negative, but the idiom is wrong regardless of that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Keep Initializr templates compatible with the pinned runtime

Every Initializr source archive now imports com.codename1.annotations.buildhints and uses the new annotations, while GeneratorModel.CN1_PLUGIN_VERSION still rewrites generated projects to 7.0.267, whose codenameone-core predates that package; the generated common POM also omits process-annotations. Consequently all newly downloaded Initializr projects fail compilation instead of receiving the defaults removed from common.zip's settings file. Leave these templates property-backed until Initializr targets the release containing this feature, or bump the generated version and bind the processor.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 339 seconds

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 62ms / native 3ms = 20.6x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathactive (NEON-accelerated)
Base64 CN1 encode167.000 ms
Base64 CN1 decode120.000 ms
Base64 native encode624.000 ms
Base64 encode ratio (CN1/native)0.268x (73.2% faster)
Base64 native decode231.000 ms
Base64 decode ratio (CN1/native)0.519x (48.1% faster)
Base64 SIMD encode56.000 ms
Base64 encode ratio (SIMD/CN1)0.335x (66.5% faster)
Base64 SIMD decode45.000 ms
Base64 decode ratio (SIMD/CN1)0.375x (62.5% faster)
Base64 encode ratio (SIMD/native)0.090x (91.0% faster)
Base64 decode ratio (SIMD/native)0.195x (80.5% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)6.000 ms
Image createMask (SIMD on)2.000 ms
Image createMask ratio (SIMD on/off)0.333x (66.7% faster)
Image applyMask (SIMD off)46.000 ms
Image applyMask (SIMD on)36.000 ms
Image applyMask ratio (SIMD on/off)0.783x (21.7% faster)
Image modifyAlpha (SIMD off)39.000 ms
Image modifyAlpha (SIMD on)39.000 ms
Image modifyAlpha ratio (SIMD on/off)1.000x (0.0% slower)
Image modifyAlpha removeColor (SIMD off)47.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.681x (31.9% faster)

Every project the archetype and the initializr produce is pinned to a released
Codename One version -- the initializr hardcodes 7.0.267 in
GeneratorModel.CN1_PLUGIN_VERSION -- and no released core carries
com.codename1.annotations.buildhints. So a generated project would import
annotations that do not resolve and fail to compile before the user has written
a line, and the settings those templates stopped declaring would simply be gone.
The templates are reverted to exactly their previous state: the archetype's
__mainName__.java and codenameone_settings.properties, and the initializr's
common.zip and four source archives. They can move to annotations in a follow-up
once a release containing the package is out.
The generated build hint table is dropped from the agent skill reference for the
same reason -- it documented a form those projects cannot use yet -- so the
generator no longer rewrites markdown at all.
What stays from that area is unrelated to annotations: the skill reference
described build hints that no builder reads, so a reader copying them got a
green build and no effect. android.xPermissions is spelled android.xpermissions,
android.minSdkVersion is android.min_sdk_version, and android.sdkVersion,
android.googlePlayVersion, build.compile, build.timeout, javascript.html5,
javascript.bundleResources and ios.orientation do not exist at all. Those
corrections are right for the published version too, and the catalog gate now
holds our own documentation to them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
compilesJava() already withheld the conventional src/main/java from the search
when the POM chain switches default-compile off with <phase>none</phase> and
binds nothing in its place. <sourceDirectory> is the same root, declared instead
of assumed, and it was not gated -- so a module that compiles nothing with javac
still offered it, and a stale copy of the main class there answered ahead of the
compiled source.
Per element rather than over the whole list, because a Kotlin-only module is
exactly the case where javac does not run and its <sourceDirs> must still be
searched. Both directions are asserted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:705783d9b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/release-on-maven-central.yml
shai-almogand others added 10 commits August 27, 2026 11:08
…writes
The .java branch in write() guarded against a non-ASCII character reaching a
generated Java source; the generator writes a JSON data file and an asciidoc
table and nothing else. toAscii survives because the reader test still needs it
-- the catalog's prose came from the developer guide and the annotations are
ASCII Java sources, so the two cannot be compared verbatim -- and its
documentation now says that rather than describing a build step it no longer
feeds.
Also the HintGroup local in Bindings that nothing read, and the no-op continue
at the end of the loop that was its only use. toHint already resolves the group
and throws when an annotation type names none.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Central recovery poll queried the two build hint artifacts with a single
curl carrying two URLs. curl needs an -o per URL and emits --write-out after
each transfer, so the second POM body arrived on stdout with both status codes
appended: the captured value could never equal 200, and the poll -- which exists
to rescue a release Central has already accepted -- would exhaust all 90
attempts and fail it. Reported by codex.
Both this poll and the R2 confirmation walk a list of artifacts, and each
carried its own copy, which had already drifted: Central's omitted
codenameone-core. The list is now named once in the job environment and both
read it, with a guard that refuses an empty list rather than reporting success
for having checked nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aliasOf, deprecated, external, enterpriseOnly and link exist so an annotated hint
can say what the catalog entries already say, and none of the 87 shipped
attributes needs any of them -- so the reader's handling of all five was carried
by no test at all. Exercised now against a copy of the real annotation package
with one probe attribute added, which is what the reader compiles anyway.
Kept rather than deleted: the first annotated hint that has to be marked
deprecated needs somewhere to say so, and would otherwise be the first thing ever
to run that path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
validateScreenshotContent threw before the write, so a failing scenario left
nothing behind but a pixel count -- which is exactly what happened to the
component-inspector run that reported textPixels=738 with no image to look at.
The capture is now written as <name>.png.rejected.png first, into the directory
the workflow already uploads with if: always(), so the next occurrence can be
diagnosed instead of guessed at.
Not a fix for that failure: the check reads a fixed screen rectangle and the run
before and after it were green on the same commit, so what it saw is still
unknown. This is what makes finding out possible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e</phase>
compilesJava() answered from the first level in the chain that switched
default-compile off, so an ancestor that disables it decided for a child that
binds its own compile execution. A module that plainly does compile Java read as
one that does not, and its Java roots were then withheld from the search -- where
a Kotlin or stale copy of the main class answers for it instead.
An enabled compile binding anywhere in the chain now settles it, and it is looked
for before any disabling level is allowed to decide. Extracted to a static form
taking the chain directly, because the walk needed a project on disk and was the
untested half of a method whose two halves are separately covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
attributeOf was written to replace a substring search for combine.self, and then
looked for the attribute name anywhere inside the start tag -- so xcombine.self
would have answered for it. The same rule, reproduced one level down in the fix
for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…were masking
Two failures on the JDK 8 leg, one the cause of the other.
The module's POM binds cn1:css at process-classes and the CSS compiler opens a
JFrame, which in this container is java.awt.HeadlessException before a single
test runs. Run under xvfb-run, which this job already uses for the Ant build a
few steps later.
That failure then skipped every step after it, including "Run SpotBugs for
ByteCodeTranslator" -- so the quality report failed for a missing
ByteCodeTranslator report, which said nothing about what had actually broken.
The step now runs last among this leg's gates, which also keeps its reinstall of
the maven plugin away from the SpotBugs reports the earlier steps produce.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three groups, none of which any reviewer asked for and all of which I added off
my own red-team pass:
The Settings POM-parser rewrites -- depth-matched </plugin>, combine.self and
combine.children read off their own element, comment stripping in parentPomPath,
the compilesJava chain walk, and the <sourceDirectory> gate. These change how the
Settings tool parses arbitrary user POMs, on the strength of cases nobody
reported. The parser is back to what it was.
The simulator verifier's rejected-capture diagnostics, which have nothing to do
with build hints -- I added them while chasing a screenshot failure that predates
this branch.
The release workflow's shared RELEASE_ARTIFACTS list. Only the defect codex
reported is kept: the two build hint POMs are queried with one curl each, because
two URLs share the single -o and the captured value could never be 200.
The plugin-side cleanups that rode along in the same commit as the parser work --
the unused ProcessorContext constructors and LibraryHintMerger's duplicate
prefix -- are kept, as is the comment de-duplication.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A generated file in git conflicts on every merge and there is nothing for a hand
edit to survive in, so it should never have been checked in.
It cannot simply move to one module's target/classes: BuildHints.entries() loads
it, so every consumer of the catalog needs it on its own classpath, and the
generator lives in build-hint-tools which depends on the catalog -- the catalog
cannot render its own resource without a dependency cycle. So each of the three
modules that actually needs it renders it into its own target/classes at
process-classes: maven/javase, maven/codenameone-maven-plugin and
scripts/settings/common. exec:java with classpathScope=compile, because a
plugin-level dependency resolves from the repository rather than the reactor and
would be a chicken and egg on a clean checkout. build-hint-tools is provided
scope in the two application modules so ASM cannot reach a built app.
Verified: all three render byte-identically to the file that was committed, the
catalog jar no longer carries it, and the javase jar does.
Consequences elsewhere. gen-build-hint-annotations.sh renders to a scratch
directory, and --check no longer diffs against a committed copy -- there is none
-- but asserts the rendering still succeeds and produces the full set, which is
the failure that would otherwise reach a user as an editor with no hints in it.
check-build-hint-catalog.py read the committed path and returned an empty set
when it was absent, which failed the gate OPEN; it now reads whichever module has
rendered it and says to build one when none has. The Ant JavaSE build takes it
from maven/javase/target/classes when that build has run, and without it the
simulator keeps the hints BuildHintSchemaDefaults compiles in and says the
annotated ones are missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removing the committed data file broke two catalog tests, and I did not see it
because I verified that module with -DskipTests -- on the one module whose
structure I had just changed.
BuildHints.entries() is complete only on a classpath carrying the rendered data
file. The catalog module cannot render one for its own tests: the generator lives
in build-hint-tools, which depends on the catalog. So the two assertions that
need the complete set -- every alias resolves to a real hint, and the catalog
agrees with LibraryHintMerger on every separator it defines -- move to
build-hint-tools, which renders the data into its own test classes first. Both
fail without that render, which is checked.
What stays beside the catalog is what its own sources declare, and the class now
says so rather than looking like it covers everything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:9b07db8e93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/developer-guide-docs.yml
shai-almogand others added 3 commits August 27, 2026 14:05
…lows
Making the table generated instead of committed took away the trigger it used to
get for free: while it was checked in, editing it showed up as a docs diff. The
replacement path list named the catalog and the render script but not the two
inputs that matter most -- the annotations, whose javadoc is the Description
column verbatim, and build-hint-tools, which is the renderer itself. A PR
renaming an attribute or rewording its documentation could therefore change the
guide without ever running the AsciiDoc and Vale checks over the result.
Reported by codex.
Fixed in both copies of that list in developer-guide-docs.yml, since triggering
the workflow is not enough on its own -- the HTML and PDF build is gated on the
paths-filter as well, and the two had already drifted.
And in website-docs.yml, which is the other workflow that renders the same table
and had the same gap for the same reason. Its pull_request and push lists both
already covered CodenameOne/src/**, so only the catalog, the renderer and the
script were missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two files overlapped and both auto-merged: website-docs.yml keeps master's
website telemetry step alongside the build hint table paths added here, and
CLAUDE.md keeps master's restructure -- 163 lines cut and the GC notes moved to
vm/CLAUDE.md -- alongside this branch's build hints section.
That section is rewritten rather than merged as-is: it still described writing
cn1-build-hints.json into the tree and told the reader to run the generator to
"rewrite the data file". Nothing is written into the tree any more, so it now
says where the file is rendered instead, and the command list loses the step that
no longer does anything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
windows-tooling builds the Settings tool with -Dcodename1.platform=javase, which
activates a profile declaring exec-maven-plugin with its own plugin level
<arguments> containing a <classpath/> element. Maven merges plugin level
configuration into every execution of that plugin, so that Object landed in the
generator execution's String array:
Cannot store value into array: ... can not cast one of the elements of
java.lang.Object[] to the type of the destination array, java.lang.String
A plain local build never activates that profile, which is why four green local
runs said nothing. Reproduced with the property set, fixed with
combine.self="override" on the arguments, and re-verified against the exact
command the workflow runs. Applied to all four generator executions rather than
only the one that failed: the collision needs a second declaration of the same
plugin anywhere in the effective build, which is not a property of this module.
Also restores the simulator verifier's rejected capture. It was reverted as out
of scope, and then the component inspector scenario failed a second time with a
byte identical textPixels=738 -- twice the same number is a state, not a race --
with no image kept to say what was on screen. Two failures in 22 runs on this
branch and none in 38 on any other is too specific to guess at.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:2d2708a0c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almogand others added 5 commits August 27, 2026 14:50
BytecodeComplianceMojo rewrites class files in place -- capping a class to the
supported version, and redirecting a call the runtime does not have. The build
hint manifest records the main class's own bytes, because the simulator has no
bytecode reader and can only compare the class file itself. If the main class is
one of the rewritten ones, a manifest written before that goal describes a class
that no longer exists on disk, and the simulator reads a manifest generated
moments earlier as stale and publishes none of the annotated hints. Reported by
codex.
Every pom in this repository happens to run process-annotations after this goal,
where the stamp is taken from the rewritten bytes anyway, so nothing is broken
today -- I checked all twelve. Nothing enforces that order though, and the
failure mode is silent: hints disappear, no error. Re-stamping here as well makes
the order stop mattering, since whichever of the two runs last leaves a manifest
describing the class that is actually there. It is a no-op when there is no
manifest, which is every project that declares its hints in the properties file.
The test asserts the hazard and the repair together: capping a version changes
the class, which invalidates a stamp taken before it, and re-stamping restores
the match without touching the hints. Removing the re-stamp fails it. What the
test does not cover is the one-line call from executeImpl, which needs a
MavenProject to reach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…wants
Both predate this branch without one. check-copyright-headers is scoped to the
PR's diff, so editing them is what brought them into scope -- the gate working as
designed, not a new defect. They take the Codename One GPLv2 + Classpath
Exception header their siblings in this package carry.
I ran that gate locally before the last push and it reported success while
checking nothing: with no --base it has no diff to scope to, and "0 file(s)
passed" was the tell I read straight past. Run with --base origin/master it
checks the same 128 files CI does, and passes. check-cast-semantics.sh takes
--baseline, a ratchet file rather than a git ref, so it is whole-repo and running
it bare is valid; copyright was the only gate here with that shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guide said the compiler-checked form existed and the generated table named
the annotation for each hint, but nothing anywhere showed what one looks like. A
reader had the Annotation column and no way to turn `@Ios(pods)` into code.
Adds a worked example and the exact properties lines it replaces, then explains
the three things about the syntax that are not guessable from the table: a list
hint takes a Java array and the build joins it with that hint's own separator, a
boolean hint takes Toggle rather than boolean so that leaving it out means the
build decides, and a hint with a closed value set takes an enum. Also says
plainly that the long tail and the open-ended families stay in the properties
file and that the two forms mix freely.
Every mapping in the example is checked against the generated data rather than
written from memory: each attribute resolves to the hint name claimed, each enum
constant to the wire value claimed, and ios.pods really does join with a comma.
The Java snippet compiles against the annotations, the guide's snippet validator
passes (inline blocks are refused there, so both snippets live in docs/demos),
Vale is clean, and the chapter renders without warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LanguageTool fails the developer guide build on a single match, and I had
hyphenated it in one sentence and not the other. Caught by running the gate
locally on the rendered chapter rather than by a CI cycle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A new .java file, so the diff-scoped gate checks it. I ran that gate with a base
earlier and it passed, then added this file and pushed without re-running it --
which is exactly what my own note about running diff-scoped gates AFTER the
commit is for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6fd1406078

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadPorts/JavaSE/src/com/codename1/impl/javase/Simulator.java
A project that adds @build or @DesktopBuild to its main class but never binds
cn1:process-annotations -- an upgraded or hand-written POM -- compiles and
launches fine, emits no manifest, and the simulator returned quietly and applied
none of the annotated hints. CN1BuildMojo refuses a device build for exactly this
case, so the simulator was the one place where the hints vanished with nothing
said and local behaviour diverged from device behaviour for the same project.
Reported by codex.
It now says so. A warning rather than a refusal: the simulator's job is to start,
and what it is missing are build settings, not something it cannot run without.
Detected by scanning the main class file's bytes for the annotations package,
because the simulator has no bytecode reader and an annotation's type is in the
constant pool as a descriptor. That is wider than reading the annotation table --
a main class that merely mentions the package matches too -- which is why this
warns and does not refuse.
Three cases are asserted: annotated with no manifest warns and names both the
class and the goal, an unannotated main class is silent, and a main class absent
from the classpath is silent. The first fails if the marker check is removed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:b0f5213ef4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import com.codename1.annotations.buildhints.Toggle;

// tag::buildHintAnnotations[]
@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the documentation snippet out of application bytecode

When docs/demos is built for a device through its reactor, this source is compiled into the common module even though codenameone_settings.properties names com.codenameone.developerguide.DemoCode as the main class. CN1BuildMojo.failOnMisplacedAnnotations() rejects live build-hint annotations on every non-main class, so BuildHintAnnotationSnippet makes that build fail before submission; binding process-annotations would instead fail during process-classes. Store the include outside the compiled source root or render it from a noncompiled snippet.

Useful? React with 👍 / 👎.

The developer guide's quality gate counts Vale alerts at suggestion level and
fails on any of them. I checked the chapter locally with --minAlertLevel=error,
which hides warnings, so two Microsoft.Adverbs warnings -- "deliberately" and
"freely" -- went through and reddened the build. Both sentences say the same
thing without the adverb.
Re-checked at the level CI uses: Vale 0/0/0, LanguageTool 0 matches, snippet
validator 706 blocks, paragraph capitalization clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog
shai-almog merged commit d21a389 into masterAug 27, 2026
72 checks passed
@shai-almog
shai-almog deleted the build-hint-annotations branch August 27, 2026 17:13
shai-almog added a commit that referenced this pull request Aug 27, 2026
Two conflicts. .gitignore had a new entry on each side and keeps both.
Advanced-Topics-Under-The-Hood.asciidoc is the one that mattered: master's #5586
replaced the hand-written build hint table with an include generated from
maven/build-hint-catalog, while this branch had added 28 macos.* rows to that
table. Resolving to master's include alone would have compiled cleanly and
silently deleted the documentation for every macOS build hint the port added --
the table is generated now, so a hint absent from the catalog has no
documentation anywhere.
So the 28 hints move into BuildHintsApple with the descriptions they had in the
table, and the generated table carries all 28 again. Confirmed by running
scripts/gen-build-hint-table.sh and counting them in the output, not by reading
the diff.
macNative.iosMinDeploymentTarget was already in the catalog and is NOT
duplicated; the first pass nearly added it back because a name pattern stopped
at the underscore in macos.add_libs and mis-parsed that row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Aug 27, 2026
master's #5586 made annotations the source for build hints that have one, and
the merge landed this port's 28 macos.* hints as hand-written catalog entries --
in HintGroup.MAC_NATIVE, whose key prefix is macNative. and which carries no
annotation at all. They belonged in the feature, not beside it.
HintGroup gains MAC_OS("Mac", "macos."), and @Mac declares all 28 as
compile-checked attributes: Toggle for the nine booleans, an appendable String[]
for addLibs following the ios.add_libs shape, and an explicit name for the
fifteen whose tail has dots and cannot be derived from a method name. The
hand-written entries are gone, because two sources for one hint is the drift
this feature exists to remove. macNative.* stays hand written: it is the
spelling the legacy Catalyst target reads and no annotation offers it.
Verified by regenerating rather than by reading the diff -- 28 macos rows in the
guide table, macos.entitlements.appSandbox typed boolean, and the catalog and
tools tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@shai-almog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Check build hints at compile time instead of shipping them inert - #5586

Merged
shai-almog merged 244 commits into
masterfrom
build-hint-annotations
Aug 27, 2026
Merged

Check build hints at compile time instead of shipping them inert#5586
shai-almog merged 244 commits into
masterfrom
build-hint-annotations

Conversation

@shai-almog

@shai-almogshai-almog commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

The problem

A build hint is a codename1.arg.<name>=<value> line that reaches a builder as request.getArg(name, default). Nothing checked the name, so a misspelling was accepted, copied into the build request, never read, and silently discarded — a green build with the setting simply not applied.

Our own agent reference had been shipping keys in exactly that state:

Documented in skill/references/build-hints.mdActually read by the builders
android.xPermissionsandroid.xpermissions (AndroidGradleBuilder.java:1206)
android.minSdkVersionandroid.min_sdk_version
android.sdkVersion(nothing — android.targetSDKVersion is the real one)

The change

87 hints can now be written as annotations on the application's main class, where javac does the checking: a misspelled name is an unknown symbol, a wrong value type is a type error, and a value outside a hint's supported set is an unknown enum constant.

@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)
@Build(nativeTheme = ThemeMode.MODERN)
@DesktopBuild(titleBar = DesktopTitleBar.NATIVE, width = 1280, height = 800)
@Ios(themeMode = ThemeMode.MODERN, newStorageLocation = Toggle.ON)
publicclassMyApplicationextendsLifecycle {
}

Seven annotations — @Ios, @Android, @DesktopBuild, @Build, @Hardening, @IosPrivacy, @OnDeviceDebug — and ten enums.

No attribute has a default that means anything

Every default is a marker for "nothing was said": Toggle.DEFAULT, "", {}, 0, or an enum's @HintUnset constant. An attribute left out is absent from the class file, so the processor emits nothing for it and the build decides, exactly as it does for a hint nobody wrote.

This is the reason there are no boolean attributes left. boolean appBundle() default false reads as "off unless you turn it on" while AndroidGradleBuilder defaults android.appBundle to true — and a copy of the server's answer compiled into every app already built cannot follow the server when it changes. Toggle is the three-state replacement, and a test refuses any hint attribute that declares a value-bearing default.

The builders are untouched.BuildHintAnnotationProcessor converts the annotations back into the same key/value pairs, and CN1BuildMojo merges them before the command-line overlay, the CN1Lib merges and both preflights — so a library still appends onto an annotation-supplied value and -D still wins. Simulator publishes them as system properties at startup so cn1:run sees hints that no longer live in the properties file.

The properties file is untouched too. It stays the way to set the long tail and the open-ended families such as android.permission.<NAME> that an annotation cannot express, with no new warnings or errors on that path. Declaring one hint both ways is a build error.

Where a hint is declared

Exactly one of two places:

  • CodenameOne/src/com/codename1/annotations/buildhints if it has an annotation. These are hand-written and are the source of truth for the hints they expose; BuildHintAnnotationReader reads them back with ASM rather than any file restating them.
  • maven/build-hint-catalog otherwise — dynamic families, build-service-only hints, the long tail.

The hint set used to be described in five places that had drifted apart: a prose table in the developer guide, a runtime scraper of that table in the Settings tool that guessed each type by string-matching the description, a fifteen-entry schema in the simulator, a fourteen-entry separator map in the plugin, and a hand-written agent reference. Only 147 of ~520 names appeared in more than one.

Nothing is generated into the tree

No generated file is committed. BuildHintCodeGenerator renders two views, both during a build:

  • cn1-build-hints.json for the two editors that are Codename One apps and so have no bytecode reader — the Settings tool and the simulator's hint editor. Each module that needs it renders it into its own target/classes (maven/javase, maven/codenameone-maven-plugin, scripts/settings/common). The catalog cannot render its own, because the generator lives in build-hint-tools, which depends on the catalog.
  • the developer guide's table, rendered when the guide is built.

Anything that can read bytecode reads the annotations directly and never touches the data file.

The guide's table goes from 208 rows to 570 with no prose lost, and gains Type, Default and Annotation columns it never had. An annotated hint's Default reads "set by the build" rather than a value, which is the honest statement of the paragraph above.

Enums are emitted only where the accepted set is demonstrable from the code that reads the hint — HardeningPreflight rejects an unknown harden.level, IOSDependencyManager throws on an unknown ios.dependencyManager, and GenerateDesktopAppWrapperMojo silently falls back to native on an unknown desktop.titleBar, which is precisely the failure this removes.

Scope: generated projects are deliberately not migrated here

Every project the archetype and the initializr produce is pinned to a released Codename One version — the initializr hardcodes 7.0.267 in GeneratorModel.CN1_PLUGIN_VERSION — and no released core carries com.codename1.annotations.buildhints. A generated project would import annotations that do not resolve and fail to compile before the user has written a line.

So the archetype's __mainName__.java and codenameone_settings.properties, and the initializr's common.zip and four source archives, are unchanged. They move to annotations in a follow-up once a release containing the package is out. scripts/skindesigner (7.0.255) stays on properties for the same reason, and cn1:migrate-build-hints refuses any project whose core lacks the package.

The in-repo tool projects that build against the snapshot from source are migrated: gamebuilder, video-builder, cn1playground, certificatewizard, guibuilder, fidelity-app, purchase-test-app, settings, hellocodenameone. protocol-e2e is not in that list: its only hint is codename1.arg.java.version, which has to stay in the properties file because the compiler needs it in order to compile the class that would otherwise declare it.

docs/demos is deliberately excluded: it is the developer guide's snippet project, full of intentionally incomplete fragments, and running the annotation processors over it fails by design.

Settings tool

It no longer scrapes the guide's AsciiDoc and guesses types; it reads the catalog. It also validates closed value domains, and refuses to edit a hint an annotation already owns — reading META-INF/codenameone/build-hints.properties and showing "Set by @Ios(themeMode) on the main class" — because writing a property for such a hint would create the duplicate declaration that fails the next build.

Its own tests had never run anywhere: both workflows that touched the module passed -Dmaven.test.skip=true, so every test written for the POM reader and the hint editor was dead weight. They now run in PR CI, 161 of them.

Gates

  • scripts/check-build-hint-catalog.sh fails when code reads a hint the catalog does not describe, and when our own docs or project templates name one that no builder reads. Its baseline is empty, so it is a hard gate rather than a ratchet. 497 hints read by the builders, all described.
  • scripts/gen-build-hint-annotations.sh --check asserts the render still succeeds and produces the full set. It is not a drift check — there is no committed copy to drift from — but a render that silently produced nothing would otherwise reach a user as an editor with no hints in it.
  • A JUnit suite checks the catalog's own consistency (attribute-name legality including the JLS 9.6.1 Object/Annotation method-name rule, enum domains, separators matching what LibraryHintMerger used to define), and build-hint-tools asserts the invariants that need the complete hint set, since that only exists on a classpath carrying the rendered data.
  • BytecodeComplianceMojo re-stamps the manifest after its in-place class rewrites, so the order of process-annotations against it stops mattering.
  • The developer-guide and website workflows list every input the table is rendered from — the annotations, the catalog, the renderer and the script — so a reworded attribute cannot change published documentation without running the AsciiDoc and Vale checks.

A build hint is a `codename1.arg.<name>=<value>` line that reaches a builder as
`request.getArg(name, default)`. Nothing checked the name, so a misspelling was
accepted, copied into the build request, never read, and silently discarded: a
green build with the setting simply not applied.
Our own agent reference had been shipping `android.xPermissions`,
`android.minSdkVersion` and `android.sdkVersion` for exactly that reason. The
builders read `android.xpermissions`, `android.min_sdk_version`, and nothing at
all.
Most hints can now be written as annotations on the application's main class,
where javac does the checking: a misspelled name is an unknown symbol, a wrong
value type is a type error, and a value outside a hint's supported set is an
unknown enum constant.
@iOS(newStorageLocation = true, themeMode = IosThemeMode.MODERN)
@android(minSdkVersion = 24, useAndroidX = true)
@desktop(titleBar = DesktopTitleBar.NATIVE)
public class MyApplication extends Lifecycle {
}
The builders are untouched: `BuildHintAnnotationProcessor` converts the
annotations back into the same key/value pairs and `CN1BuildMojo` merges them
before the command-line overlay, the CN1Lib merges and both preflights, so a
library still appends onto an annotation-supplied value and `-D` still wins.
`Simulator` publishes them as system properties at startup so `cn1:run` sees
hints that no longer live in the properties file.
The properties file is untouched too. It stays the way to set the long tail and
the open-ended families such as `android.permission.<NAME>` that an annotation
cannot express, with no new warnings or errors. Declaring one hint both ways is
a build error.
One catalog, five generated views
---------------------------------
The hint set had been described in five places that had drifted apart: a prose
table in the developer guide, a runtime scraper of that table in the Settings
tool that guessed each type by string-matching the description, a fifteen-entry
schema in the simulator, a fourteen-entry separator map in the plugin, and a
hand-written agent reference. Only 147 of ~520 names appeared in more than one.
`maven/build-hint-catalog` is now the single source of truth (529 hints: 457
mined from the builders, 56 documented-but-unread, 16 dynamic families; 82
exposed as annotation attributes). The annotations, the binding table the
processor reads back, the guide's table, the simulator's editor schema and the
agent reference are all generated from it. The guide's table goes from 208 rows
to 529 with no prose lost.
Enums are emitted only where the accepted set is demonstrable from the code that
reads the hint -- `HardeningPreflight` rejects an unknown `harden.level`,
`IOSDependencyManager` throws on an unknown `ios.dependencyManager`, and
`GenerateDesktopAppWrapperMojo` silently falls back to `native` on an unknown
`desktop.titleBar`, which is the failure this removes.
Generated projects
------------------
The archetype and all four initializr templates now carry the annotations, and
`cn1:migrate-build-hints` moves an existing project over. Eleven in-repo
projects are migrated. `java.version` deliberately stays in the properties file:
it picks the toolchain that compiles the class the annotations live on.
Gates
-----
`scripts/check-build-hint-catalog.sh` fails when code reads a hint the catalog
does not describe, and when our own docs or templates name one that no builder
reads. Its baseline is empty, so it is a hard gate rather than a ratchet.
`scripts/gen-build-hint-annotations.sh --check` fails on generated-file drift.
Both run in the Java 8 leg of PR CI.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:8d2cfcfde3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadmaven/pom.xml
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs[Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • build-hint-catalog: 0 findings (no issues)
    • build-hint-tools: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

`.gitignore` carries a repo-wide `**/build/*`. The catalog's package is
`com.codename1.build.shared`, so all 13 of its sources sat under a path segment
named `build` and `git add` silently skipped them. Only `pom.xml` was committed:
the module built locally from the working tree and produced an empty jar in CI,
which is why `codenameone-maven-plugin` then failed with `cannot find symbol` on
`BuildHints` and nearly every job went red.
The sibling `platform-feature-catalog` lives in the same package and is fine,
because it was added before that rule existed -- tracked files stay tracked, so
nothing ever pointed at the hazard.
Un-ignore `build` when it is a Java package rather than a build output
directory, with the rationale beside the rule so the next file added there is
not lost the same way. `maven/core/build/*` and `CodenameOne/build/*` stay
ignored.
Also from review:
- Every bare `open()` in the four Python scripts now uses a context manager, so
the handle closes even if parsing or `json.dump` raises, and the writes state
their encoding.
- The generator no longer emits an IP literal as an annotation default. PMD
reads `default "127.0.0.1"` as hardcoded configuration, and the default clause
is documentation only -- the processor emits a hint solely for members the
developer actually wrote -- so the value moves to the javadoc where it belongs.
- Files the migration touched that never carried a copyright header now have the
complete one. The archetype's `__mainName__.java` is excluded instead: it is a
template for the user's own application class, and stamping a Codename One GPL
header onto it would put our licence on their code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Pin generated projects to a version containing the annotations

The updated Initializr starter archives now import and use com.codename1.annotations.buildhints, but GeneratorModel.java:44 still generates projects pinned to CN1 7.0.267, whose core artifact predates this package. Consequently every newly generated barebones, Kotlin, Grub, or Tweet project fails compilation on the unresolved annotations unless the user manually changes the CN1 version; either defer these template changes or update the generated runtime/plugin version to the first release containing them.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The archived bootstrap ran generation at module scope, so gen_external.py's
`import gen_catalog` -- which only wants three helper functions -- rewrote every
catalog source as a side effect. Generation and its diagnostics now live in
`main()` behind a `__main__` guard, and the module-level file reads became
`load_license()` / `load_mined()` / `load_docs()`, so importing does no I/O and
cannot fail on inputs the archived copy deliberately does not carry.
Verified both directions: importing leaves the catalog untouched, and running
the two scripts end to end still reproduces the committed catalog byte for byte.
Also drops `json` and `subprocess` from check-build-hint-catalog.py. Both were
left from an earlier version that shelled out to the miner instead of importing
it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:d727c7d976

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadscripts/gen-build-hint-annotations.sh Outdated
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendscalar fallback (no native SIMD)
SIMD int-add (64K x300)java 196ms / native 73ms = 2.6x speedup
SIMD float-mul (64K x300)java 144ms / native 89ms = 1.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode83.000 ms
Base64 CN1 decode86.000 ms
Base64 native encode391.000 ms
Base64 encode ratio (CN1/native)0.212x (78.8% faster)
Base64 native decode310.000 ms
Base64 decode ratio (CN1/native)0.277x (72.3% faster)
Image encode benchmark statusskipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 60ms / native 4ms = 15.0x speedup
SIMD float-mul (64K x300)java 80ms / native 3ms = 26.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode190.000 ms
Base64 CN1 decode381.000 ms
Base64 SIMD encode99.000 ms
Base64 encode ratio (SIMD/CN1)0.521x (47.9% faster)
Base64 SIMD decode91.000 ms
Base64 decode ratio (SIMD/CN1)0.239x (76.1% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)22.000 ms
Image createMask (SIMD on)15.000 ms
Image createMask ratio (SIMD on/off)0.682x (31.8% faster)
Image applyMask (SIMD off)71.000 ms
Image applyMask (SIMD on)35.000 ms
Image applyMask ratio (SIMD on/off)0.493x (50.7% faster)
Image modifyAlpha (SIMD off)44.000 ms
Image modifyAlpha (SIMD on)22.000 ms
Image modifyAlpha ratio (SIMD on/off)0.500x (50.0% faster)
Image modifyAlpha removeColor (SIMD off)34.000 ms
Image modifyAlpha removeColor (SIMD on)25.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.735x (26.5% faster)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 63ms / native 5ms = 12.6x speedup
SIMD float-mul (64K x300)java 63ms / native 4ms = 15.7x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode327.000 ms
Base64 CN1 decode216.000 ms
Base64 SIMD encode176.000 ms
Base64 encode ratio (SIMD/CN1)0.538x (46.2% faster)
Base64 SIMD decode132.000 ms
Base64 decode ratio (SIMD/CN1)0.611x (38.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)28.000 ms
Image createMask (SIMD on)23.000 ms
Image createMask ratio (SIMD on/off)0.821x (17.9% faster)
Image applyMask (SIMD off)58.000 ms
Image applyMask (SIMD on)56.000 ms
Image applyMask ratio (SIMD on/off)0.966x (3.4% faster)
Image modifyAlpha (SIMD off)66.000 ms
Image modifyAlpha (SIMD on)28.000 ms
Image modifyAlpha ratio (SIMD on/off)0.424x (57.6% faster)
Image modifyAlpha removeColor (SIMD off)44.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.727x (27.3% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 55ms / native 3ms = 18.3x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode268.000 ms
Base64 CN1 decode152.000 ms
Base64 SIMD encode66.000 ms
Base64 encode ratio (SIMD/CN1)0.246x (75.4% faster)
Base64 SIMD decode64.000 ms
Base64 decode ratio (SIMD/CN1)0.421x (57.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)13.000 ms
Image createMask (SIMD on)9.000 ms
Image createMask ratio (SIMD on/off)0.692x (30.8% faster)
Image applyMask (SIMD off)24.000 ms
Image applyMask (SIMD on)20.000 ms
Image applyMask ratio (SIMD on/off)0.833x (16.7% faster)
Image modifyAlpha (SIMD off)17.000 ms
Image modifyAlpha (SIMD on)12.000 ms
Image modifyAlpha ratio (SIMD on/off)0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off)21.000 ms
Image modifyAlpha removeColor (SIMD on)13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.619x (38.1% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

Three separate gates rejected generated output. Each is fixed in the generator
so the class of problem cannot come back through a catalog edit.
Unmappable characters. The prose is imported from the developer guide, which
uses typographic punctuation, and `CodenameOne/src` is also compiled by an Ant
javac step with ASCII encoding where a single em dash is
`error: unmappable character for encoding ASCII` -- a build failure, not a
warning. A Unicode escape would not have helped: javac expands `\uXXXX` before
it strips comments, so the character reappears. `toAscii` now folds the
punctuation that actually occurs, and *refuses* anything it has no mapping for
rather than dropping it, because silently deleting a character from a hint's
documentation is the worse outcome.
Broken table. `ios.spm.packages` is documented as `identity|url|requirement`,
and a bare `|` starts a new AsciiDoc cell, so asciidoctor reported "dropping
cells from incomplete row" for the whole 529-row table. Cells are escaped now.
Vale. The guide enforces the Microsoft style as errors, and the generated table
feeds it, so the catalog's prose has to satisfy it too: contractions, no
"and so on", no stray adverbs. A default value is not prose, though -- the one
remaining hit was `android.file_paths`, whose default is an XML fragment -- so
a quoted default now carries the `// vale-skip:` comment .vale.ini documents
for individual false positives.
Also fixes a data bug the guide exposed. The miner preserved Java escape
sequences instead of decoding them, so `android.file_paths` and
`android.facebook_permissions` recorded defaults containing literal
backslashes that the build never sees, and those reached the rendered table.
The miner decodes escapes and re-quotes safely, and the two catalog entries are
corrected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/build_hint_miner.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:96bff9038a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

`killedThreadReportsItselfFinished` failed the Java 21 leg with
"FormTest timed out after 5000ms; edt=initialized pendingSerialCalls=0". The
waits in this class used a 5000ms deadline, which is exactly the `@FormTest`
timeout in EDTTestInterceptor -- so on a loaded runner the poll loop consumed
the entire harness budget and the interceptor fired first. The report then said
only that the method timed out, with nothing about which condition never became
true.
The waits now use 2000ms, well inside the harness budget and still roughly two
thousand times the ~1ms these threads actually take to stop. A genuine
regression now fails on the test's own assertion, which names what went wrong.
Pre-existing (the test arrived with #5526) and unrelated to the build hint work:
core-unittests has no dependency on the JavaSE port, so none of the simulator
registration in this branch runs there, this branch changes nothing under
com.codename1.db or EasyThread, and the Java 8 leg passed the same commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:a343fe3335

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

A mojo's defaultPhase does not bind it to a project -- the project's POM has to
-- and nothing turns a build hint annotation back into a codename1.arg.* pair
except the process-annotations goal. So migrating a project without that
binding deleted working properties and replaced them with annotations no goal
ever reads: the hints vanished from the build with no diagnostic anywhere.
Five projects in this branch were already in that state. gamebuilder,
docs/demos, video-builder and cn1playground bind the plugin but not that goal,
so the binding is added. input-validation-app's common module has no build
section at all, so its migration is reverted rather than inventing a lifecycle
for a demo app.
The goal now checks the reactor for the binding and refuses with the execution
block to paste, so this cannot happen to anyone else.
Three more from the same review:
- The deletion pass recognized only `key=value`. `Properties.load` also accepts
`key:value`, `key value`, escaped separators inside the key, and logical
continuation lines; a declaration it failed to match was left behind while the
annotation was added, so the next build failed with the duplicate-hint error
this goal exists to prevent. Keys are parsed the way Properties.load defines
them now, with a unit test per form.
- The settings file was read as ISO-8859-1 and written back as UTF-8, turning
any unrelated non-ASCII byte -- an accented displayName, say -- into mojibake.
It is written back as ISO-8859-1.
- cn1.androidTheme and cn1.nativeTheme are deprecated aliases of and.themeMode
and nativeTheme, which the builders honour as fallbacks. Neither declared
aliasOf, so conflict detection missed them and one value silently won.
Also: the generation script rebuilt the generator only when its class was
absent, so editing a catalog source and rerunning regenerated every view from
the previous build's bytecode -- reporting success while ignoring the edit, and
passing --check on a tree that was genuinely stale. It always rebuilds now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:0edef42ca4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…plicating a hint
docs/demos is the developer guide's snippet project: deliberately incomplete
code fragments that illustrate @entity, @route, @AppIntent and @mapped. Binding
process-annotations there put those snippets in front of the other processors,
which correctly rejected six of them, so the migration is reverted and its two
hints are back in the properties file. That the project omitted the goal was the
point, not an oversight.
The other three newly bound projects were checked rather than assumed:
gamebuilder, video-builder and cn1playground each run process-annotations
cleanly and emit 6, 3 and 5 hints respectively.
Settings could still create the duplicate the migration is careful to avoid. In
a generated project ios.themeMode and its neighbours are annotations, but the
Build Hints UI decides a hint is inactive from the properties file alone and its
Add button writes a property -- producing a second declaration that fails the
next build. The tool now reads META-INF/codenameone/build-hints.properties, the
file the processor writes on every build and deletes when the last annotation
goes, and renders those hints read-only with the attribute that owns them:
"Set by @iOS(themeMode) on the main class." An unbuilt project has no such file
and behaves as before.
Also fixes the SpotBugs finding this branch introduced: `backslashes % 2 == 1`
in the continuation scan is false for negative odd numbers, so it is `!= 0`.
The count cannot go negative, but the idiom is wrong regardless of that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Keep Initializr templates compatible with the pinned runtime

Every Initializr source archive now imports com.codename1.annotations.buildhints and uses the new annotations, while GeneratorModel.CN1_PLUGIN_VERSION still rewrites generated projects to 7.0.267, whose codenameone-core predates that package; the generated common POM also omits process-annotations. Consequently all newly downloaded Initializr projects fail compilation instead of receiving the defaults removed from common.zip's settings file. Leave these templates property-backed until Initializr targets the release containing this feature, or bump the generated version and bind the processor.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 339 seconds

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 62ms / native 3ms = 20.6x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathactive (NEON-accelerated)
Base64 CN1 encode167.000 ms
Base64 CN1 decode120.000 ms
Base64 native encode624.000 ms
Base64 encode ratio (CN1/native)0.268x (73.2% faster)
Base64 native decode231.000 ms
Base64 decode ratio (CN1/native)0.519x (48.1% faster)
Base64 SIMD encode56.000 ms
Base64 encode ratio (SIMD/CN1)0.335x (66.5% faster)
Base64 SIMD decode45.000 ms
Base64 decode ratio (SIMD/CN1)0.375x (62.5% faster)
Base64 encode ratio (SIMD/native)0.090x (91.0% faster)
Base64 decode ratio (SIMD/native)0.195x (80.5% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)6.000 ms
Image createMask (SIMD on)2.000 ms
Image createMask ratio (SIMD on/off)0.333x (66.7% faster)
Image applyMask (SIMD off)46.000 ms
Image applyMask (SIMD on)36.000 ms
Image applyMask ratio (SIMD on/off)0.783x (21.7% faster)
Image modifyAlpha (SIMD off)39.000 ms
Image modifyAlpha (SIMD on)39.000 ms
Image modifyAlpha ratio (SIMD on/off)1.000x (0.0% slower)
Image modifyAlpha removeColor (SIMD off)47.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.681x (31.9% faster)

Every project the archetype and the initializr produce is pinned to a released
Codename One version -- the initializr hardcodes 7.0.267 in
GeneratorModel.CN1_PLUGIN_VERSION -- and no released core carries
com.codename1.annotations.buildhints. So a generated project would import
annotations that do not resolve and fail to compile before the user has written
a line, and the settings those templates stopped declaring would simply be gone.
The templates are reverted to exactly their previous state: the archetype's
__mainName__.java and codenameone_settings.properties, and the initializr's
common.zip and four source archives. They can move to annotations in a follow-up
once a release containing the package is out.
The generated build hint table is dropped from the agent skill reference for the
same reason -- it documented a form those projects cannot use yet -- so the
generator no longer rewrites markdown at all.
What stays from that area is unrelated to annotations: the skill reference
described build hints that no builder reads, so a reader copying them got a
green build and no effect. android.xPermissions is spelled android.xpermissions,
android.minSdkVersion is android.min_sdk_version, and android.sdkVersion,
android.googlePlayVersion, build.compile, build.timeout, javascript.html5,
javascript.bundleResources and ios.orientation do not exist at all. Those
corrections are right for the published version too, and the catalog gate now
holds our own documentation to them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
compilesJava() already withheld the conventional src/main/java from the search
when the POM chain switches default-compile off with <phase>none</phase> and
binds nothing in its place. <sourceDirectory> is the same root, declared instead
of assumed, and it was not gated -- so a module that compiles nothing with javac
still offered it, and a stale copy of the main class there answered ahead of the
compiled source.
Per element rather than over the whole list, because a Kotlin-only module is
exactly the case where javac does not run and its <sourceDirs> must still be
searched. Both directions are asserted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:705783d9b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/release-on-maven-central.yml
shai-almogand others added 10 commits August 27, 2026 11:08
…writes
The .java branch in write() guarded against a non-ASCII character reaching a
generated Java source; the generator writes a JSON data file and an asciidoc
table and nothing else. toAscii survives because the reader test still needs it
-- the catalog's prose came from the developer guide and the annotations are
ASCII Java sources, so the two cannot be compared verbatim -- and its
documentation now says that rather than describing a build step it no longer
feeds.
Also the HintGroup local in Bindings that nothing read, and the no-op continue
at the end of the loop that was its only use. toHint already resolves the group
and throws when an annotation type names none.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Central recovery poll queried the two build hint artifacts with a single
curl carrying two URLs. curl needs an -o per URL and emits --write-out after
each transfer, so the second POM body arrived on stdout with both status codes
appended: the captured value could never equal 200, and the poll -- which exists
to rescue a release Central has already accepted -- would exhaust all 90
attempts and fail it. Reported by codex.
Both this poll and the R2 confirmation walk a list of artifacts, and each
carried its own copy, which had already drifted: Central's omitted
codenameone-core. The list is now named once in the job environment and both
read it, with a guard that refuses an empty list rather than reporting success
for having checked nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aliasOf, deprecated, external, enterpriseOnly and link exist so an annotated hint
can say what the catalog entries already say, and none of the 87 shipped
attributes needs any of them -- so the reader's handling of all five was carried
by no test at all. Exercised now against a copy of the real annotation package
with one probe attribute added, which is what the reader compiles anyway.
Kept rather than deleted: the first annotated hint that has to be marked
deprecated needs somewhere to say so, and would otherwise be the first thing ever
to run that path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
validateScreenshotContent threw before the write, so a failing scenario left
nothing behind but a pixel count -- which is exactly what happened to the
component-inspector run that reported textPixels=738 with no image to look at.
The capture is now written as <name>.png.rejected.png first, into the directory
the workflow already uploads with if: always(), so the next occurrence can be
diagnosed instead of guessed at.
Not a fix for that failure: the check reads a fixed screen rectangle and the run
before and after it were green on the same commit, so what it saw is still
unknown. This is what makes finding out possible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e</phase>
compilesJava() answered from the first level in the chain that switched
default-compile off, so an ancestor that disables it decided for a child that
binds its own compile execution. A module that plainly does compile Java read as
one that does not, and its Java roots were then withheld from the search -- where
a Kotlin or stale copy of the main class answers for it instead.
An enabled compile binding anywhere in the chain now settles it, and it is looked
for before any disabling level is allowed to decide. Extracted to a static form
taking the chain directly, because the walk needed a project on disk and was the
untested half of a method whose two halves are separately covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
attributeOf was written to replace a substring search for combine.self, and then
looked for the attribute name anywhere inside the start tag -- so xcombine.self
would have answered for it. The same rule, reproduced one level down in the fix
for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…were masking
Two failures on the JDK 8 leg, one the cause of the other.
The module's POM binds cn1:css at process-classes and the CSS compiler opens a
JFrame, which in this container is java.awt.HeadlessException before a single
test runs. Run under xvfb-run, which this job already uses for the Ant build a
few steps later.
That failure then skipped every step after it, including "Run SpotBugs for
ByteCodeTranslator" -- so the quality report failed for a missing
ByteCodeTranslator report, which said nothing about what had actually broken.
The step now runs last among this leg's gates, which also keeps its reinstall of
the maven plugin away from the SpotBugs reports the earlier steps produce.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three groups, none of which any reviewer asked for and all of which I added off
my own red-team pass:
The Settings POM-parser rewrites -- depth-matched </plugin>, combine.self and
combine.children read off their own element, comment stripping in parentPomPath,
the compilesJava chain walk, and the <sourceDirectory> gate. These change how the
Settings tool parses arbitrary user POMs, on the strength of cases nobody
reported. The parser is back to what it was.
The simulator verifier's rejected-capture diagnostics, which have nothing to do
with build hints -- I added them while chasing a screenshot failure that predates
this branch.
The release workflow's shared RELEASE_ARTIFACTS list. Only the defect codex
reported is kept: the two build hint POMs are queried with one curl each, because
two URLs share the single -o and the captured value could never be 200.
The plugin-side cleanups that rode along in the same commit as the parser work --
the unused ProcessorContext constructors and LibraryHintMerger's duplicate
prefix -- are kept, as is the comment de-duplication.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A generated file in git conflicts on every merge and there is nothing for a hand
edit to survive in, so it should never have been checked in.
It cannot simply move to one module's target/classes: BuildHints.entries() loads
it, so every consumer of the catalog needs it on its own classpath, and the
generator lives in build-hint-tools which depends on the catalog -- the catalog
cannot render its own resource without a dependency cycle. So each of the three
modules that actually needs it renders it into its own target/classes at
process-classes: maven/javase, maven/codenameone-maven-plugin and
scripts/settings/common. exec:java with classpathScope=compile, because a
plugin-level dependency resolves from the repository rather than the reactor and
would be a chicken and egg on a clean checkout. build-hint-tools is provided
scope in the two application modules so ASM cannot reach a built app.
Verified: all three render byte-identically to the file that was committed, the
catalog jar no longer carries it, and the javase jar does.
Consequences elsewhere. gen-build-hint-annotations.sh renders to a scratch
directory, and --check no longer diffs against a committed copy -- there is none
-- but asserts the rendering still succeeds and produces the full set, which is
the failure that would otherwise reach a user as an editor with no hints in it.
check-build-hint-catalog.py read the committed path and returned an empty set
when it was absent, which failed the gate OPEN; it now reads whichever module has
rendered it and says to build one when none has. The Ant JavaSE build takes it
from maven/javase/target/classes when that build has run, and without it the
simulator keeps the hints BuildHintSchemaDefaults compiles in and says the
annotated ones are missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removing the committed data file broke two catalog tests, and I did not see it
because I verified that module with -DskipTests -- on the one module whose
structure I had just changed.
BuildHints.entries() is complete only on a classpath carrying the rendered data
file. The catalog module cannot render one for its own tests: the generator lives
in build-hint-tools, which depends on the catalog. So the two assertions that
need the complete set -- every alias resolves to a real hint, and the catalog
agrees with LibraryHintMerger on every separator it defines -- move to
build-hint-tools, which renders the data into its own test classes first. Both
fail without that render, which is checked.
What stays beside the catalog is what its own sources declare, and the class now
says so rather than looking like it covers everything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:9b07db8e93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/developer-guide-docs.yml
shai-almogand others added 3 commits August 27, 2026 14:05
…lows
Making the table generated instead of committed took away the trigger it used to
get for free: while it was checked in, editing it showed up as a docs diff. The
replacement path list named the catalog and the render script but not the two
inputs that matter most -- the annotations, whose javadoc is the Description
column verbatim, and build-hint-tools, which is the renderer itself. A PR
renaming an attribute or rewording its documentation could therefore change the
guide without ever running the AsciiDoc and Vale checks over the result.
Reported by codex.
Fixed in both copies of that list in developer-guide-docs.yml, since triggering
the workflow is not enough on its own -- the HTML and PDF build is gated on the
paths-filter as well, and the two had already drifted.
And in website-docs.yml, which is the other workflow that renders the same table
and had the same gap for the same reason. Its pull_request and push lists both
already covered CodenameOne/src/**, so only the catalog, the renderer and the
script were missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two files overlapped and both auto-merged: website-docs.yml keeps master's
website telemetry step alongside the build hint table paths added here, and
CLAUDE.md keeps master's restructure -- 163 lines cut and the GC notes moved to
vm/CLAUDE.md -- alongside this branch's build hints section.
That section is rewritten rather than merged as-is: it still described writing
cn1-build-hints.json into the tree and told the reader to run the generator to
"rewrite the data file". Nothing is written into the tree any more, so it now
says where the file is rendered instead, and the command list loses the step that
no longer does anything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
windows-tooling builds the Settings tool with -Dcodename1.platform=javase, which
activates a profile declaring exec-maven-plugin with its own plugin level
<arguments> containing a <classpath/> element. Maven merges plugin level
configuration into every execution of that plugin, so that Object landed in the
generator execution's String array:
Cannot store value into array: ... can not cast one of the elements of
java.lang.Object[] to the type of the destination array, java.lang.String
A plain local build never activates that profile, which is why four green local
runs said nothing. Reproduced with the property set, fixed with
combine.self="override" on the arguments, and re-verified against the exact
command the workflow runs. Applied to all four generator executions rather than
only the one that failed: the collision needs a second declaration of the same
plugin anywhere in the effective build, which is not a property of this module.
Also restores the simulator verifier's rejected capture. It was reverted as out
of scope, and then the component inspector scenario failed a second time with a
byte identical textPixels=738 -- twice the same number is a state, not a race --
with no image kept to say what was on screen. Two failures in 22 runs on this
branch and none in 38 on any other is too specific to guess at.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:2d2708a0c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almogand others added 5 commits August 27, 2026 14:50
BytecodeComplianceMojo rewrites class files in place -- capping a class to the
supported version, and redirecting a call the runtime does not have. The build
hint manifest records the main class's own bytes, because the simulator has no
bytecode reader and can only compare the class file itself. If the main class is
one of the rewritten ones, a manifest written before that goal describes a class
that no longer exists on disk, and the simulator reads a manifest generated
moments earlier as stale and publishes none of the annotated hints. Reported by
codex.
Every pom in this repository happens to run process-annotations after this goal,
where the stamp is taken from the rewritten bytes anyway, so nothing is broken
today -- I checked all twelve. Nothing enforces that order though, and the
failure mode is silent: hints disappear, no error. Re-stamping here as well makes
the order stop mattering, since whichever of the two runs last leaves a manifest
describing the class that is actually there. It is a no-op when there is no
manifest, which is every project that declares its hints in the properties file.
The test asserts the hazard and the repair together: capping a version changes
the class, which invalidates a stamp taken before it, and re-stamping restores
the match without touching the hints. Removing the re-stamp fails it. What the
test does not cover is the one-line call from executeImpl, which needs a
MavenProject to reach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…wants
Both predate this branch without one. check-copyright-headers is scoped to the
PR's diff, so editing them is what brought them into scope -- the gate working as
designed, not a new defect. They take the Codename One GPLv2 + Classpath
Exception header their siblings in this package carry.
I ran that gate locally before the last push and it reported success while
checking nothing: with no --base it has no diff to scope to, and "0 file(s)
passed" was the tell I read straight past. Run with --base origin/master it
checks the same 128 files CI does, and passes. check-cast-semantics.sh takes
--baseline, a ratchet file rather than a git ref, so it is whole-repo and running
it bare is valid; copyright was the only gate here with that shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guide said the compiler-checked form existed and the generated table named
the annotation for each hint, but nothing anywhere showed what one looks like. A
reader had the Annotation column and no way to turn `@Ios(pods)` into code.
Adds a worked example and the exact properties lines it replaces, then explains
the three things about the syntax that are not guessable from the table: a list
hint takes a Java array and the build joins it with that hint's own separator, a
boolean hint takes Toggle rather than boolean so that leaving it out means the
build decides, and a hint with a closed value set takes an enum. Also says
plainly that the long tail and the open-ended families stay in the properties
file and that the two forms mix freely.
Every mapping in the example is checked against the generated data rather than
written from memory: each attribute resolves to the hint name claimed, each enum
constant to the wire value claimed, and ios.pods really does join with a comma.
The Java snippet compiles against the annotations, the guide's snippet validator
passes (inline blocks are refused there, so both snippets live in docs/demos),
Vale is clean, and the chapter renders without warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LanguageTool fails the developer guide build on a single match, and I had
hyphenated it in one sentence and not the other. Caught by running the gate
locally on the rendered chapter rather than by a CI cycle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A new .java file, so the diff-scoped gate checks it. I ran that gate with a base
earlier and it passed, then added this file and pushed without re-running it --
which is exactly what my own note about running diff-scoped gates AFTER the
commit is for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6fd1406078

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadPorts/JavaSE/src/com/codename1/impl/javase/Simulator.java
A project that adds @build or @DesktopBuild to its main class but never binds
cn1:process-annotations -- an upgraded or hand-written POM -- compiles and
launches fine, emits no manifest, and the simulator returned quietly and applied
none of the annotated hints. CN1BuildMojo refuses a device build for exactly this
case, so the simulator was the one place where the hints vanished with nothing
said and local behaviour diverged from device behaviour for the same project.
Reported by codex.
It now says so. A warning rather than a refusal: the simulator's job is to start,
and what it is missing are build settings, not something it cannot run without.
Detected by scanning the main class file's bytes for the annotations package,
because the simulator has no bytecode reader and an annotation's type is in the
constant pool as a descriptor. That is wider than reading the annotation table --
a main class that merely mentions the package matches too -- which is why this
warns and does not refuse.
Three cases are asserted: annotated with no manifest warns and names both the
class and the goal, an unannotated main class is silent, and a main class absent
from the classpath is silent. The first fails if the marker check is removed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:b0f5213ef4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import com.codename1.annotations.buildhints.Toggle;

// tag::buildHintAnnotations[]
@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the documentation snippet out of application bytecode

When docs/demos is built for a device through its reactor, this source is compiled into the common module even though codenameone_settings.properties names com.codenameone.developerguide.DemoCode as the main class. CN1BuildMojo.failOnMisplacedAnnotations() rejects live build-hint annotations on every non-main class, so BuildHintAnnotationSnippet makes that build fail before submission; binding process-annotations would instead fail during process-classes. Store the include outside the compiled source root or render it from a noncompiled snippet.

Useful? React with 👍 / 👎.

The developer guide's quality gate counts Vale alerts at suggestion level and
fails on any of them. I checked the chapter locally with --minAlertLevel=error,
which hides warnings, so two Microsoft.Adverbs warnings -- "deliberately" and
"freely" -- went through and reddened the build. Both sentences say the same
thing without the adverb.
Re-checked at the level CI uses: Vale 0/0/0, LanguageTool 0 matches, snippet
validator 706 blocks, paragraph capitalization clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog
shai-almog merged commit d21a389 into masterAug 27, 2026
72 checks passed
@shai-almog
shai-almog deleted the build-hint-annotations branch August 27, 2026 17:13
shai-almog added a commit that referenced this pull request Aug 27, 2026
Two conflicts. .gitignore had a new entry on each side and keeps both.
Advanced-Topics-Under-The-Hood.asciidoc is the one that mattered: master's #5586
replaced the hand-written build hint table with an include generated from
maven/build-hint-catalog, while this branch had added 28 macos.* rows to that
table. Resolving to master's include alone would have compiled cleanly and
silently deleted the documentation for every macOS build hint the port added --
the table is generated now, so a hint absent from the catalog has no
documentation anywhere.
So the 28 hints move into BuildHintsApple with the descriptions they had in the
table, and the generated table carries all 28 again. Confirmed by running
scripts/gen-build-hint-table.sh and counting them in the output, not by reading
the diff.
macNative.iosMinDeploymentTarget was already in the catalog and is NOT
duplicated; the first pass nearly added it back because a name pattern stopped
at the underscore in macos.add_libs and mis-parsed that row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Aug 27, 2026
master's #5586 made annotations the source for build hints that have one, and
the merge landed this port's 28 macos.* hints as hand-written catalog entries --
in HintGroup.MAC_NATIVE, whose key prefix is macNative. and which carries no
annotation at all. They belonged in the feature, not beside it.
HintGroup gains MAC_OS("Mac", "macos."), and @Mac declares all 28 as
compile-checked attributes: Toggle for the nine booleans, an appendable String[]
for addLibs following the ios.add_libs shape, and an explicit name for the
fifteen whose tail has dots and cannot be derived from a method name. The
hand-written entries are gone, because two sources for one hint is the drift
this feature exists to remove. macNative.* stays hand written: it is the
spelling the legacy Catalyst target reads and no annotation offers it.
Verified by regenerating rather than by reading the diff -- 28 macos rows in the
guide table, macos.entitlements.appSandbox typed boolean, and the catalog and
tools tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@shai-almog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Check build hints at compile time instead of shipping them inert - #5586

Merged
shai-almog merged 244 commits into
masterfrom
build-hint-annotations
Aug 27, 2026
Merged

Check build hints at compile time instead of shipping them inert#5586
shai-almog merged 244 commits into
masterfrom
build-hint-annotations

Conversation

@shai-almog

@shai-almogshai-almog commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

The problem

A build hint is a codename1.arg.<name>=<value> line that reaches a builder as request.getArg(name, default). Nothing checked the name, so a misspelling was accepted, copied into the build request, never read, and silently discarded — a green build with the setting simply not applied.

Our own agent reference had been shipping keys in exactly that state:

Documented in skill/references/build-hints.mdActually read by the builders
android.xPermissionsandroid.xpermissions (AndroidGradleBuilder.java:1206)
android.minSdkVersionandroid.min_sdk_version
android.sdkVersion(nothing — android.targetSDKVersion is the real one)

The change

87 hints can now be written as annotations on the application's main class, where javac does the checking: a misspelled name is an unknown symbol, a wrong value type is a type error, and a value outside a hint's supported set is an unknown enum constant.

@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)
@Build(nativeTheme = ThemeMode.MODERN)
@DesktopBuild(titleBar = DesktopTitleBar.NATIVE, width = 1280, height = 800)
@Ios(themeMode = ThemeMode.MODERN, newStorageLocation = Toggle.ON)
publicclassMyApplicationextendsLifecycle {
}

Seven annotations — @Ios, @Android, @DesktopBuild, @Build, @Hardening, @IosPrivacy, @OnDeviceDebug — and ten enums.

No attribute has a default that means anything

Every default is a marker for "nothing was said": Toggle.DEFAULT, "", {}, 0, or an enum's @HintUnset constant. An attribute left out is absent from the class file, so the processor emits nothing for it and the build decides, exactly as it does for a hint nobody wrote.

This is the reason there are no boolean attributes left. boolean appBundle() default false reads as "off unless you turn it on" while AndroidGradleBuilder defaults android.appBundle to true — and a copy of the server's answer compiled into every app already built cannot follow the server when it changes. Toggle is the three-state replacement, and a test refuses any hint attribute that declares a value-bearing default.

The builders are untouched.BuildHintAnnotationProcessor converts the annotations back into the same key/value pairs, and CN1BuildMojo merges them before the command-line overlay, the CN1Lib merges and both preflights — so a library still appends onto an annotation-supplied value and -D still wins. Simulator publishes them as system properties at startup so cn1:run sees hints that no longer live in the properties file.

The properties file is untouched too. It stays the way to set the long tail and the open-ended families such as android.permission.<NAME> that an annotation cannot express, with no new warnings or errors on that path. Declaring one hint both ways is a build error.

Where a hint is declared

Exactly one of two places:

  • CodenameOne/src/com/codename1/annotations/buildhints if it has an annotation. These are hand-written and are the source of truth for the hints they expose; BuildHintAnnotationReader reads them back with ASM rather than any file restating them.
  • maven/build-hint-catalog otherwise — dynamic families, build-service-only hints, the long tail.

The hint set used to be described in five places that had drifted apart: a prose table in the developer guide, a runtime scraper of that table in the Settings tool that guessed each type by string-matching the description, a fifteen-entry schema in the simulator, a fourteen-entry separator map in the plugin, and a hand-written agent reference. Only 147 of ~520 names appeared in more than one.

Nothing is generated into the tree

No generated file is committed. BuildHintCodeGenerator renders two views, both during a build:

  • cn1-build-hints.json for the two editors that are Codename One apps and so have no bytecode reader — the Settings tool and the simulator's hint editor. Each module that needs it renders it into its own target/classes (maven/javase, maven/codenameone-maven-plugin, scripts/settings/common). The catalog cannot render its own, because the generator lives in build-hint-tools, which depends on the catalog.
  • the developer guide's table, rendered when the guide is built.

Anything that can read bytecode reads the annotations directly and never touches the data file.

The guide's table goes from 208 rows to 570 with no prose lost, and gains Type, Default and Annotation columns it never had. An annotated hint's Default reads "set by the build" rather than a value, which is the honest statement of the paragraph above.

Enums are emitted only where the accepted set is demonstrable from the code that reads the hint — HardeningPreflight rejects an unknown harden.level, IOSDependencyManager throws on an unknown ios.dependencyManager, and GenerateDesktopAppWrapperMojo silently falls back to native on an unknown desktop.titleBar, which is precisely the failure this removes.

Scope: generated projects are deliberately not migrated here

Every project the archetype and the initializr produce is pinned to a released Codename One version — the initializr hardcodes 7.0.267 in GeneratorModel.CN1_PLUGIN_VERSION — and no released core carries com.codename1.annotations.buildhints. A generated project would import annotations that do not resolve and fail to compile before the user has written a line.

So the archetype's __mainName__.java and codenameone_settings.properties, and the initializr's common.zip and four source archives, are unchanged. They move to annotations in a follow-up once a release containing the package is out. scripts/skindesigner (7.0.255) stays on properties for the same reason, and cn1:migrate-build-hints refuses any project whose core lacks the package.

The in-repo tool projects that build against the snapshot from source are migrated: gamebuilder, video-builder, cn1playground, certificatewizard, guibuilder, fidelity-app, purchase-test-app, settings, hellocodenameone. protocol-e2e is not in that list: its only hint is codename1.arg.java.version, which has to stay in the properties file because the compiler needs it in order to compile the class that would otherwise declare it.

docs/demos is deliberately excluded: it is the developer guide's snippet project, full of intentionally incomplete fragments, and running the annotation processors over it fails by design.

Settings tool

It no longer scrapes the guide's AsciiDoc and guesses types; it reads the catalog. It also validates closed value domains, and refuses to edit a hint an annotation already owns — reading META-INF/codenameone/build-hints.properties and showing "Set by @Ios(themeMode) on the main class" — because writing a property for such a hint would create the duplicate declaration that fails the next build.

Its own tests had never run anywhere: both workflows that touched the module passed -Dmaven.test.skip=true, so every test written for the POM reader and the hint editor was dead weight. They now run in PR CI, 161 of them.

Gates

  • scripts/check-build-hint-catalog.sh fails when code reads a hint the catalog does not describe, and when our own docs or project templates name one that no builder reads. Its baseline is empty, so it is a hard gate rather than a ratchet. 497 hints read by the builders, all described.
  • scripts/gen-build-hint-annotations.sh --check asserts the render still succeeds and produces the full set. It is not a drift check — there is no committed copy to drift from — but a render that silently produced nothing would otherwise reach a user as an editor with no hints in it.
  • A JUnit suite checks the catalog's own consistency (attribute-name legality including the JLS 9.6.1 Object/Annotation method-name rule, enum domains, separators matching what LibraryHintMerger used to define), and build-hint-tools asserts the invariants that need the complete hint set, since that only exists on a classpath carrying the rendered data.
  • BytecodeComplianceMojo re-stamps the manifest after its in-place class rewrites, so the order of process-annotations against it stops mattering.
  • The developer-guide and website workflows list every input the table is rendered from — the annotations, the catalog, the renderer and the script — so a reworded attribute cannot change published documentation without running the AsciiDoc and Vale checks.

A build hint is a `codename1.arg.<name>=<value>` line that reaches a builder as
`request.getArg(name, default)`. Nothing checked the name, so a misspelling was
accepted, copied into the build request, never read, and silently discarded: a
green build with the setting simply not applied.
Our own agent reference had been shipping `android.xPermissions`,
`android.minSdkVersion` and `android.sdkVersion` for exactly that reason. The
builders read `android.xpermissions`, `android.min_sdk_version`, and nothing at
all.
Most hints can now be written as annotations on the application's main class,
where javac does the checking: a misspelled name is an unknown symbol, a wrong
value type is a type error, and a value outside a hint's supported set is an
unknown enum constant.
@iOS(newStorageLocation = true, themeMode = IosThemeMode.MODERN)
@android(minSdkVersion = 24, useAndroidX = true)
@desktop(titleBar = DesktopTitleBar.NATIVE)
public class MyApplication extends Lifecycle {
}
The builders are untouched: `BuildHintAnnotationProcessor` converts the
annotations back into the same key/value pairs and `CN1BuildMojo` merges them
before the command-line overlay, the CN1Lib merges and both preflights, so a
library still appends onto an annotation-supplied value and `-D` still wins.
`Simulator` publishes them as system properties at startup so `cn1:run` sees
hints that no longer live in the properties file.
The properties file is untouched too. It stays the way to set the long tail and
the open-ended families such as `android.permission.<NAME>` that an annotation
cannot express, with no new warnings or errors. Declaring one hint both ways is
a build error.
One catalog, five generated views
---------------------------------
The hint set had been described in five places that had drifted apart: a prose
table in the developer guide, a runtime scraper of that table in the Settings
tool that guessed each type by string-matching the description, a fifteen-entry
schema in the simulator, a fourteen-entry separator map in the plugin, and a
hand-written agent reference. Only 147 of ~520 names appeared in more than one.
`maven/build-hint-catalog` is now the single source of truth (529 hints: 457
mined from the builders, 56 documented-but-unread, 16 dynamic families; 82
exposed as annotation attributes). The annotations, the binding table the
processor reads back, the guide's table, the simulator's editor schema and the
agent reference are all generated from it. The guide's table goes from 208 rows
to 529 with no prose lost.
Enums are emitted only where the accepted set is demonstrable from the code that
reads the hint -- `HardeningPreflight` rejects an unknown `harden.level`,
`IOSDependencyManager` throws on an unknown `ios.dependencyManager`, and
`GenerateDesktopAppWrapperMojo` silently falls back to `native` on an unknown
`desktop.titleBar`, which is the failure this removes.
Generated projects
------------------
The archetype and all four initializr templates now carry the annotations, and
`cn1:migrate-build-hints` moves an existing project over. Eleven in-repo
projects are migrated. `java.version` deliberately stays in the properties file:
it picks the toolchain that compiles the class the annotations live on.
Gates
-----
`scripts/check-build-hint-catalog.sh` fails when code reads a hint the catalog
does not describe, and when our own docs or templates name one that no builder
reads. Its baseline is empty, so it is a hard gate rather than a ratchet.
`scripts/gen-build-hint-annotations.sh --check` fails on generated-file drift.
Both run in the Java 8 leg of PR CI.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:8d2cfcfde3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadmaven/pom.xml
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs[Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • build-hint-catalog: 0 findings (no issues)
    • build-hint-tools: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

`.gitignore` carries a repo-wide `**/build/*`. The catalog's package is
`com.codename1.build.shared`, so all 13 of its sources sat under a path segment
named `build` and `git add` silently skipped them. Only `pom.xml` was committed:
the module built locally from the working tree and produced an empty jar in CI,
which is why `codenameone-maven-plugin` then failed with `cannot find symbol` on
`BuildHints` and nearly every job went red.
The sibling `platform-feature-catalog` lives in the same package and is fine,
because it was added before that rule existed -- tracked files stay tracked, so
nothing ever pointed at the hazard.
Un-ignore `build` when it is a Java package rather than a build output
directory, with the rationale beside the rule so the next file added there is
not lost the same way. `maven/core/build/*` and `CodenameOne/build/*` stay
ignored.
Also from review:
- Every bare `open()` in the four Python scripts now uses a context manager, so
the handle closes even if parsing or `json.dump` raises, and the writes state
their encoding.
- The generator no longer emits an IP literal as an annotation default. PMD
reads `default "127.0.0.1"` as hardcoded configuration, and the default clause
is documentation only -- the processor emits a hint solely for members the
developer actually wrote -- so the value moves to the javadoc where it belongs.
- Files the migration touched that never carried a copyright header now have the
complete one. The archetype's `__mainName__.java` is excluded instead: it is a
template for the user's own application class, and stamping a Codename One GPL
header onto it would put our licence on their code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Pin generated projects to a version containing the annotations

The updated Initializr starter archives now import and use com.codename1.annotations.buildhints, but GeneratorModel.java:44 still generates projects pinned to CN1 7.0.267, whose core artifact predates this package. Consequently every newly generated barebones, Kotlin, Grub, or Tweet project fails compilation on the unresolved annotations unless the user manually changes the CN1 version; either defer these template changes or update the generated runtime/plugin version to the first release containing them.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The archived bootstrap ran generation at module scope, so gen_external.py's
`import gen_catalog` -- which only wants three helper functions -- rewrote every
catalog source as a side effect. Generation and its diagnostics now live in
`main()` behind a `__main__` guard, and the module-level file reads became
`load_license()` / `load_mined()` / `load_docs()`, so importing does no I/O and
cannot fail on inputs the archived copy deliberately does not carry.
Verified both directions: importing leaves the catalog untouched, and running
the two scripts end to end still reproduces the committed catalog byte for byte.
Also drops `json` and `subprocess` from check-build-hint-catalog.py. Both were
left from an earlier version that shelled out to the miner instead of importing
it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:d727c7d976

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadscripts/gen-build-hint-annotations.sh Outdated
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendscalar fallback (no native SIMD)
SIMD int-add (64K x300)java 196ms / native 73ms = 2.6x speedup
SIMD float-mul (64K x300)java 144ms / native 89ms = 1.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode83.000 ms
Base64 CN1 decode86.000 ms
Base64 native encode391.000 ms
Base64 encode ratio (CN1/native)0.212x (78.8% faster)
Base64 native decode310.000 ms
Base64 decode ratio (CN1/native)0.277x (72.3% faster)
Image encode benchmark statusskipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 60ms / native 4ms = 15.0x speedup
SIMD float-mul (64K x300)java 80ms / native 3ms = 26.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode190.000 ms
Base64 CN1 decode381.000 ms
Base64 SIMD encode99.000 ms
Base64 encode ratio (SIMD/CN1)0.521x (47.9% faster)
Base64 SIMD decode91.000 ms
Base64 decode ratio (SIMD/CN1)0.239x (76.1% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)22.000 ms
Image createMask (SIMD on)15.000 ms
Image createMask ratio (SIMD on/off)0.682x (31.8% faster)
Image applyMask (SIMD off)71.000 ms
Image applyMask (SIMD on)35.000 ms
Image applyMask ratio (SIMD on/off)0.493x (50.7% faster)
Image modifyAlpha (SIMD off)44.000 ms
Image modifyAlpha (SIMD on)22.000 ms
Image modifyAlpha ratio (SIMD on/off)0.500x (50.0% faster)
Image modifyAlpha removeColor (SIMD off)34.000 ms
Image modifyAlpha removeColor (SIMD on)25.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.735x (26.5% faster)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 63ms / native 5ms = 12.6x speedup
SIMD float-mul (64K x300)java 63ms / native 4ms = 15.7x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode327.000 ms
Base64 CN1 decode216.000 ms
Base64 SIMD encode176.000 ms
Base64 encode ratio (SIMD/CN1)0.538x (46.2% faster)
Base64 SIMD decode132.000 ms
Base64 decode ratio (SIMD/CN1)0.611x (38.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)28.000 ms
Image createMask (SIMD on)23.000 ms
Image createMask ratio (SIMD on/off)0.821x (17.9% faster)
Image applyMask (SIMD off)58.000 ms
Image applyMask (SIMD on)56.000 ms
Image applyMask ratio (SIMD on/off)0.966x (3.4% faster)
Image modifyAlpha (SIMD off)66.000 ms
Image modifyAlpha (SIMD on)28.000 ms
Image modifyAlpha ratio (SIMD on/off)0.424x (57.6% faster)
Image modifyAlpha removeColor (SIMD off)44.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.727x (27.3% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 55ms / native 3ms = 18.3x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode268.000 ms
Base64 CN1 decode152.000 ms
Base64 SIMD encode66.000 ms
Base64 encode ratio (SIMD/CN1)0.246x (75.4% faster)
Base64 SIMD decode64.000 ms
Base64 decode ratio (SIMD/CN1)0.421x (57.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)13.000 ms
Image createMask (SIMD on)9.000 ms
Image createMask ratio (SIMD on/off)0.692x (30.8% faster)
Image applyMask (SIMD off)24.000 ms
Image applyMask (SIMD on)20.000 ms
Image applyMask ratio (SIMD on/off)0.833x (16.7% faster)
Image modifyAlpha (SIMD off)17.000 ms
Image modifyAlpha (SIMD on)12.000 ms
Image modifyAlpha ratio (SIMD on/off)0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off)21.000 ms
Image modifyAlpha removeColor (SIMD on)13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.619x (38.1% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

Three separate gates rejected generated output. Each is fixed in the generator
so the class of problem cannot come back through a catalog edit.
Unmappable characters. The prose is imported from the developer guide, which
uses typographic punctuation, and `CodenameOne/src` is also compiled by an Ant
javac step with ASCII encoding where a single em dash is
`error: unmappable character for encoding ASCII` -- a build failure, not a
warning. A Unicode escape would not have helped: javac expands `\uXXXX` before
it strips comments, so the character reappears. `toAscii` now folds the
punctuation that actually occurs, and *refuses* anything it has no mapping for
rather than dropping it, because silently deleting a character from a hint's
documentation is the worse outcome.
Broken table. `ios.spm.packages` is documented as `identity|url|requirement`,
and a bare `|` starts a new AsciiDoc cell, so asciidoctor reported "dropping
cells from incomplete row" for the whole 529-row table. Cells are escaped now.
Vale. The guide enforces the Microsoft style as errors, and the generated table
feeds it, so the catalog's prose has to satisfy it too: contractions, no
"and so on", no stray adverbs. A default value is not prose, though -- the one
remaining hit was `android.file_paths`, whose default is an XML fragment -- so
a quoted default now carries the `// vale-skip:` comment .vale.ini documents
for individual false positives.
Also fixes a data bug the guide exposed. The miner preserved Java escape
sequences instead of decoding them, so `android.file_paths` and
`android.facebook_permissions` recorded defaults containing literal
backslashes that the build never sees, and those reached the rendered table.
The miner decodes escapes and re-quotes safely, and the two catalog entries are
corrected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/build_hint_miner.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:96bff9038a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

`killedThreadReportsItselfFinished` failed the Java 21 leg with
"FormTest timed out after 5000ms; edt=initialized pendingSerialCalls=0". The
waits in this class used a 5000ms deadline, which is exactly the `@FormTest`
timeout in EDTTestInterceptor -- so on a loaded runner the poll loop consumed
the entire harness budget and the interceptor fired first. The report then said
only that the method timed out, with nothing about which condition never became
true.
The waits now use 2000ms, well inside the harness budget and still roughly two
thousand times the ~1ms these threads actually take to stop. A genuine
regression now fails on the test's own assertion, which names what went wrong.
Pre-existing (the test arrived with #5526) and unrelated to the build hint work:
core-unittests has no dependency on the JavaSE port, so none of the simulator
registration in this branch runs there, this branch changes nothing under
com.codename1.db or EasyThread, and the Java 8 leg passed the same commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:a343fe3335

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

A mojo's defaultPhase does not bind it to a project -- the project's POM has to
-- and nothing turns a build hint annotation back into a codename1.arg.* pair
except the process-annotations goal. So migrating a project without that
binding deleted working properties and replaced them with annotations no goal
ever reads: the hints vanished from the build with no diagnostic anywhere.
Five projects in this branch were already in that state. gamebuilder,
docs/demos, video-builder and cn1playground bind the plugin but not that goal,
so the binding is added. input-validation-app's common module has no build
section at all, so its migration is reverted rather than inventing a lifecycle
for a demo app.
The goal now checks the reactor for the binding and refuses with the execution
block to paste, so this cannot happen to anyone else.
Three more from the same review:
- The deletion pass recognized only `key=value`. `Properties.load` also accepts
`key:value`, `key value`, escaped separators inside the key, and logical
continuation lines; a declaration it failed to match was left behind while the
annotation was added, so the next build failed with the duplicate-hint error
this goal exists to prevent. Keys are parsed the way Properties.load defines
them now, with a unit test per form.
- The settings file was read as ISO-8859-1 and written back as UTF-8, turning
any unrelated non-ASCII byte -- an accented displayName, say -- into mojibake.
It is written back as ISO-8859-1.
- cn1.androidTheme and cn1.nativeTheme are deprecated aliases of and.themeMode
and nativeTheme, which the builders honour as fallbacks. Neither declared
aliasOf, so conflict detection missed them and one value silently won.
Also: the generation script rebuilt the generator only when its class was
absent, so editing a catalog source and rerunning regenerated every view from
the previous build's bytecode -- reporting success while ignoring the edit, and
passing --check on a tree that was genuinely stale. It always rebuilds now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:0edef42ca4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…plicating a hint
docs/demos is the developer guide's snippet project: deliberately incomplete
code fragments that illustrate @entity, @route, @AppIntent and @mapped. Binding
process-annotations there put those snippets in front of the other processors,
which correctly rejected six of them, so the migration is reverted and its two
hints are back in the properties file. That the project omitted the goal was the
point, not an oversight.
The other three newly bound projects were checked rather than assumed:
gamebuilder, video-builder and cn1playground each run process-annotations
cleanly and emit 6, 3 and 5 hints respectively.
Settings could still create the duplicate the migration is careful to avoid. In
a generated project ios.themeMode and its neighbours are annotations, but the
Build Hints UI decides a hint is inactive from the properties file alone and its
Add button writes a property -- producing a second declaration that fails the
next build. The tool now reads META-INF/codenameone/build-hints.properties, the
file the processor writes on every build and deletes when the last annotation
goes, and renders those hints read-only with the attribute that owns them:
"Set by @iOS(themeMode) on the main class." An unbuilt project has no such file
and behaves as before.
Also fixes the SpotBugs finding this branch introduced: `backslashes % 2 == 1`
in the continuation scan is false for negative odd numbers, so it is `!= 0`.
The count cannot go negative, but the idiom is wrong regardless of that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Keep Initializr templates compatible with the pinned runtime

Every Initializr source archive now imports com.codename1.annotations.buildhints and uses the new annotations, while GeneratorModel.CN1_PLUGIN_VERSION still rewrites generated projects to 7.0.267, whose codenameone-core predates that package; the generated common POM also omits process-annotations. Consequently all newly downloaded Initializr projects fail compilation instead of receiving the defaults removed from common.zip's settings file. Leave these templates property-backed until Initializr targets the release containing this feature, or bump the generated version and bind the processor.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 339 seconds

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 62ms / native 3ms = 20.6x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathactive (NEON-accelerated)
Base64 CN1 encode167.000 ms
Base64 CN1 decode120.000 ms
Base64 native encode624.000 ms
Base64 encode ratio (CN1/native)0.268x (73.2% faster)
Base64 native decode231.000 ms
Base64 decode ratio (CN1/native)0.519x (48.1% faster)
Base64 SIMD encode56.000 ms
Base64 encode ratio (SIMD/CN1)0.335x (66.5% faster)
Base64 SIMD decode45.000 ms
Base64 decode ratio (SIMD/CN1)0.375x (62.5% faster)
Base64 encode ratio (SIMD/native)0.090x (91.0% faster)
Base64 decode ratio (SIMD/native)0.195x (80.5% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)6.000 ms
Image createMask (SIMD on)2.000 ms
Image createMask ratio (SIMD on/off)0.333x (66.7% faster)
Image applyMask (SIMD off)46.000 ms
Image applyMask (SIMD on)36.000 ms
Image applyMask ratio (SIMD on/off)0.783x (21.7% faster)
Image modifyAlpha (SIMD off)39.000 ms
Image modifyAlpha (SIMD on)39.000 ms
Image modifyAlpha ratio (SIMD on/off)1.000x (0.0% slower)
Image modifyAlpha removeColor (SIMD off)47.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.681x (31.9% faster)

Every project the archetype and the initializr produce is pinned to a released
Codename One version -- the initializr hardcodes 7.0.267 in
GeneratorModel.CN1_PLUGIN_VERSION -- and no released core carries
com.codename1.annotations.buildhints. So a generated project would import
annotations that do not resolve and fail to compile before the user has written
a line, and the settings those templates stopped declaring would simply be gone.
The templates are reverted to exactly their previous state: the archetype's
__mainName__.java and codenameone_settings.properties, and the initializr's
common.zip and four source archives. They can move to annotations in a follow-up
once a release containing the package is out.
The generated build hint table is dropped from the agent skill reference for the
same reason -- it documented a form those projects cannot use yet -- so the
generator no longer rewrites markdown at all.
What stays from that area is unrelated to annotations: the skill reference
described build hints that no builder reads, so a reader copying them got a
green build and no effect. android.xPermissions is spelled android.xpermissions,
android.minSdkVersion is android.min_sdk_version, and android.sdkVersion,
android.googlePlayVersion, build.compile, build.timeout, javascript.html5,
javascript.bundleResources and ios.orientation do not exist at all. Those
corrections are right for the published version too, and the catalog gate now
holds our own documentation to them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
compilesJava() already withheld the conventional src/main/java from the search
when the POM chain switches default-compile off with <phase>none</phase> and
binds nothing in its place. <sourceDirectory> is the same root, declared instead
of assumed, and it was not gated -- so a module that compiles nothing with javac
still offered it, and a stale copy of the main class there answered ahead of the
compiled source.
Per element rather than over the whole list, because a Kotlin-only module is
exactly the case where javac does not run and its <sourceDirs> must still be
searched. Both directions are asserted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:705783d9b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/release-on-maven-central.yml
shai-almogand others added 10 commits August 27, 2026 11:08
…writes
The .java branch in write() guarded against a non-ASCII character reaching a
generated Java source; the generator writes a JSON data file and an asciidoc
table and nothing else. toAscii survives because the reader test still needs it
-- the catalog's prose came from the developer guide and the annotations are
ASCII Java sources, so the two cannot be compared verbatim -- and its
documentation now says that rather than describing a build step it no longer
feeds.
Also the HintGroup local in Bindings that nothing read, and the no-op continue
at the end of the loop that was its only use. toHint already resolves the group
and throws when an annotation type names none.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Central recovery poll queried the two build hint artifacts with a single
curl carrying two URLs. curl needs an -o per URL and emits --write-out after
each transfer, so the second POM body arrived on stdout with both status codes
appended: the captured value could never equal 200, and the poll -- which exists
to rescue a release Central has already accepted -- would exhaust all 90
attempts and fail it. Reported by codex.
Both this poll and the R2 confirmation walk a list of artifacts, and each
carried its own copy, which had already drifted: Central's omitted
codenameone-core. The list is now named once in the job environment and both
read it, with a guard that refuses an empty list rather than reporting success
for having checked nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aliasOf, deprecated, external, enterpriseOnly and link exist so an annotated hint
can say what the catalog entries already say, and none of the 87 shipped
attributes needs any of them -- so the reader's handling of all five was carried
by no test at all. Exercised now against a copy of the real annotation package
with one probe attribute added, which is what the reader compiles anyway.
Kept rather than deleted: the first annotated hint that has to be marked
deprecated needs somewhere to say so, and would otherwise be the first thing ever
to run that path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
validateScreenshotContent threw before the write, so a failing scenario left
nothing behind but a pixel count -- which is exactly what happened to the
component-inspector run that reported textPixels=738 with no image to look at.
The capture is now written as <name>.png.rejected.png first, into the directory
the workflow already uploads with if: always(), so the next occurrence can be
diagnosed instead of guessed at.
Not a fix for that failure: the check reads a fixed screen rectangle and the run
before and after it were green on the same commit, so what it saw is still
unknown. This is what makes finding out possible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e</phase>
compilesJava() answered from the first level in the chain that switched
default-compile off, so an ancestor that disables it decided for a child that
binds its own compile execution. A module that plainly does compile Java read as
one that does not, and its Java roots were then withheld from the search -- where
a Kotlin or stale copy of the main class answers for it instead.
An enabled compile binding anywhere in the chain now settles it, and it is looked
for before any disabling level is allowed to decide. Extracted to a static form
taking the chain directly, because the walk needed a project on disk and was the
untested half of a method whose two halves are separately covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
attributeOf was written to replace a substring search for combine.self, and then
looked for the attribute name anywhere inside the start tag -- so xcombine.self
would have answered for it. The same rule, reproduced one level down in the fix
for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…were masking
Two failures on the JDK 8 leg, one the cause of the other.
The module's POM binds cn1:css at process-classes and the CSS compiler opens a
JFrame, which in this container is java.awt.HeadlessException before a single
test runs. Run under xvfb-run, which this job already uses for the Ant build a
few steps later.
That failure then skipped every step after it, including "Run SpotBugs for
ByteCodeTranslator" -- so the quality report failed for a missing
ByteCodeTranslator report, which said nothing about what had actually broken.
The step now runs last among this leg's gates, which also keeps its reinstall of
the maven plugin away from the SpotBugs reports the earlier steps produce.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three groups, none of which any reviewer asked for and all of which I added off
my own red-team pass:
The Settings POM-parser rewrites -- depth-matched </plugin>, combine.self and
combine.children read off their own element, comment stripping in parentPomPath,
the compilesJava chain walk, and the <sourceDirectory> gate. These change how the
Settings tool parses arbitrary user POMs, on the strength of cases nobody
reported. The parser is back to what it was.
The simulator verifier's rejected-capture diagnostics, which have nothing to do
with build hints -- I added them while chasing a screenshot failure that predates
this branch.
The release workflow's shared RELEASE_ARTIFACTS list. Only the defect codex
reported is kept: the two build hint POMs are queried with one curl each, because
two URLs share the single -o and the captured value could never be 200.
The plugin-side cleanups that rode along in the same commit as the parser work --
the unused ProcessorContext constructors and LibraryHintMerger's duplicate
prefix -- are kept, as is the comment de-duplication.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A generated file in git conflicts on every merge and there is nothing for a hand
edit to survive in, so it should never have been checked in.
It cannot simply move to one module's target/classes: BuildHints.entries() loads
it, so every consumer of the catalog needs it on its own classpath, and the
generator lives in build-hint-tools which depends on the catalog -- the catalog
cannot render its own resource without a dependency cycle. So each of the three
modules that actually needs it renders it into its own target/classes at
process-classes: maven/javase, maven/codenameone-maven-plugin and
scripts/settings/common. exec:java with classpathScope=compile, because a
plugin-level dependency resolves from the repository rather than the reactor and
would be a chicken and egg on a clean checkout. build-hint-tools is provided
scope in the two application modules so ASM cannot reach a built app.
Verified: all three render byte-identically to the file that was committed, the
catalog jar no longer carries it, and the javase jar does.
Consequences elsewhere. gen-build-hint-annotations.sh renders to a scratch
directory, and --check no longer diffs against a committed copy -- there is none
-- but asserts the rendering still succeeds and produces the full set, which is
the failure that would otherwise reach a user as an editor with no hints in it.
check-build-hint-catalog.py read the committed path and returned an empty set
when it was absent, which failed the gate OPEN; it now reads whichever module has
rendered it and says to build one when none has. The Ant JavaSE build takes it
from maven/javase/target/classes when that build has run, and without it the
simulator keeps the hints BuildHintSchemaDefaults compiles in and says the
annotated ones are missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removing the committed data file broke two catalog tests, and I did not see it
because I verified that module with -DskipTests -- on the one module whose
structure I had just changed.
BuildHints.entries() is complete only on a classpath carrying the rendered data
file. The catalog module cannot render one for its own tests: the generator lives
in build-hint-tools, which depends on the catalog. So the two assertions that
need the complete set -- every alias resolves to a real hint, and the catalog
agrees with LibraryHintMerger on every separator it defines -- move to
build-hint-tools, which renders the data into its own test classes first. Both
fail without that render, which is checked.
What stays beside the catalog is what its own sources declare, and the class now
says so rather than looking like it covers everything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:9b07db8e93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/developer-guide-docs.yml
shai-almogand others added 3 commits August 27, 2026 14:05
…lows
Making the table generated instead of committed took away the trigger it used to
get for free: while it was checked in, editing it showed up as a docs diff. The
replacement path list named the catalog and the render script but not the two
inputs that matter most -- the annotations, whose javadoc is the Description
column verbatim, and build-hint-tools, which is the renderer itself. A PR
renaming an attribute or rewording its documentation could therefore change the
guide without ever running the AsciiDoc and Vale checks over the result.
Reported by codex.
Fixed in both copies of that list in developer-guide-docs.yml, since triggering
the workflow is not enough on its own -- the HTML and PDF build is gated on the
paths-filter as well, and the two had already drifted.
And in website-docs.yml, which is the other workflow that renders the same table
and had the same gap for the same reason. Its pull_request and push lists both
already covered CodenameOne/src/**, so only the catalog, the renderer and the
script were missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two files overlapped and both auto-merged: website-docs.yml keeps master's
website telemetry step alongside the build hint table paths added here, and
CLAUDE.md keeps master's restructure -- 163 lines cut and the GC notes moved to
vm/CLAUDE.md -- alongside this branch's build hints section.
That section is rewritten rather than merged as-is: it still described writing
cn1-build-hints.json into the tree and told the reader to run the generator to
"rewrite the data file". Nothing is written into the tree any more, so it now
says where the file is rendered instead, and the command list loses the step that
no longer does anything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
windows-tooling builds the Settings tool with -Dcodename1.platform=javase, which
activates a profile declaring exec-maven-plugin with its own plugin level
<arguments> containing a <classpath/> element. Maven merges plugin level
configuration into every execution of that plugin, so that Object landed in the
generator execution's String array:
Cannot store value into array: ... can not cast one of the elements of
java.lang.Object[] to the type of the destination array, java.lang.String
A plain local build never activates that profile, which is why four green local
runs said nothing. Reproduced with the property set, fixed with
combine.self="override" on the arguments, and re-verified against the exact
command the workflow runs. Applied to all four generator executions rather than
only the one that failed: the collision needs a second declaration of the same
plugin anywhere in the effective build, which is not a property of this module.
Also restores the simulator verifier's rejected capture. It was reverted as out
of scope, and then the component inspector scenario failed a second time with a
byte identical textPixels=738 -- twice the same number is a state, not a race --
with no image kept to say what was on screen. Two failures in 22 runs on this
branch and none in 38 on any other is too specific to guess at.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:2d2708a0c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almogand others added 5 commits August 27, 2026 14:50
BytecodeComplianceMojo rewrites class files in place -- capping a class to the
supported version, and redirecting a call the runtime does not have. The build
hint manifest records the main class's own bytes, because the simulator has no
bytecode reader and can only compare the class file itself. If the main class is
one of the rewritten ones, a manifest written before that goal describes a class
that no longer exists on disk, and the simulator reads a manifest generated
moments earlier as stale and publishes none of the annotated hints. Reported by
codex.
Every pom in this repository happens to run process-annotations after this goal,
where the stamp is taken from the rewritten bytes anyway, so nothing is broken
today -- I checked all twelve. Nothing enforces that order though, and the
failure mode is silent: hints disappear, no error. Re-stamping here as well makes
the order stop mattering, since whichever of the two runs last leaves a manifest
describing the class that is actually there. It is a no-op when there is no
manifest, which is every project that declares its hints in the properties file.
The test asserts the hazard and the repair together: capping a version changes
the class, which invalidates a stamp taken before it, and re-stamping restores
the match without touching the hints. Removing the re-stamp fails it. What the
test does not cover is the one-line call from executeImpl, which needs a
MavenProject to reach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…wants
Both predate this branch without one. check-copyright-headers is scoped to the
PR's diff, so editing them is what brought them into scope -- the gate working as
designed, not a new defect. They take the Codename One GPLv2 + Classpath
Exception header their siblings in this package carry.
I ran that gate locally before the last push and it reported success while
checking nothing: with no --base it has no diff to scope to, and "0 file(s)
passed" was the tell I read straight past. Run with --base origin/master it
checks the same 128 files CI does, and passes. check-cast-semantics.sh takes
--baseline, a ratchet file rather than a git ref, so it is whole-repo and running
it bare is valid; copyright was the only gate here with that shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guide said the compiler-checked form existed and the generated table named
the annotation for each hint, but nothing anywhere showed what one looks like. A
reader had the Annotation column and no way to turn `@Ios(pods)` into code.
Adds a worked example and the exact properties lines it replaces, then explains
the three things about the syntax that are not guessable from the table: a list
hint takes a Java array and the build joins it with that hint's own separator, a
boolean hint takes Toggle rather than boolean so that leaving it out means the
build decides, and a hint with a closed value set takes an enum. Also says
plainly that the long tail and the open-ended families stay in the properties
file and that the two forms mix freely.
Every mapping in the example is checked against the generated data rather than
written from memory: each attribute resolves to the hint name claimed, each enum
constant to the wire value claimed, and ios.pods really does join with a comma.
The Java snippet compiles against the annotations, the guide's snippet validator
passes (inline blocks are refused there, so both snippets live in docs/demos),
Vale is clean, and the chapter renders without warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LanguageTool fails the developer guide build on a single match, and I had
hyphenated it in one sentence and not the other. Caught by running the gate
locally on the rendered chapter rather than by a CI cycle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A new .java file, so the diff-scoped gate checks it. I ran that gate with a base
earlier and it passed, then added this file and pushed without re-running it --
which is exactly what my own note about running diff-scoped gates AFTER the
commit is for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6fd1406078

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadPorts/JavaSE/src/com/codename1/impl/javase/Simulator.java
A project that adds @build or @DesktopBuild to its main class but never binds
cn1:process-annotations -- an upgraded or hand-written POM -- compiles and
launches fine, emits no manifest, and the simulator returned quietly and applied
none of the annotated hints. CN1BuildMojo refuses a device build for exactly this
case, so the simulator was the one place where the hints vanished with nothing
said and local behaviour diverged from device behaviour for the same project.
Reported by codex.
It now says so. A warning rather than a refusal: the simulator's job is to start,
and what it is missing are build settings, not something it cannot run without.
Detected by scanning the main class file's bytes for the annotations package,
because the simulator has no bytecode reader and an annotation's type is in the
constant pool as a descriptor. That is wider than reading the annotation table --
a main class that merely mentions the package matches too -- which is why this
warns and does not refuse.
Three cases are asserted: annotated with no manifest warns and names both the
class and the goal, an unannotated main class is silent, and a main class absent
from the classpath is silent. The first fails if the marker check is removed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:b0f5213ef4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import com.codename1.annotations.buildhints.Toggle;

// tag::buildHintAnnotations[]
@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the documentation snippet out of application bytecode

When docs/demos is built for a device through its reactor, this source is compiled into the common module even though codenameone_settings.properties names com.codenameone.developerguide.DemoCode as the main class. CN1BuildMojo.failOnMisplacedAnnotations() rejects live build-hint annotations on every non-main class, so BuildHintAnnotationSnippet makes that build fail before submission; binding process-annotations would instead fail during process-classes. Store the include outside the compiled source root or render it from a noncompiled snippet.

Useful? React with 👍 / 👎.

The developer guide's quality gate counts Vale alerts at suggestion level and
fails on any of them. I checked the chapter locally with --minAlertLevel=error,
which hides warnings, so two Microsoft.Adverbs warnings -- "deliberately" and
"freely" -- went through and reddened the build. Both sentences say the same
thing without the adverb.
Re-checked at the level CI uses: Vale 0/0/0, LanguageTool 0 matches, snippet
validator 706 blocks, paragraph capitalization clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog
shai-almog merged commit d21a389 into masterAug 27, 2026
72 checks passed
@shai-almog
shai-almog deleted the build-hint-annotations branch August 27, 2026 17:13
shai-almog added a commit that referenced this pull request Aug 27, 2026
Two conflicts. .gitignore had a new entry on each side and keeps both.
Advanced-Topics-Under-The-Hood.asciidoc is the one that mattered: master's #5586
replaced the hand-written build hint table with an include generated from
maven/build-hint-catalog, while this branch had added 28 macos.* rows to that
table. Resolving to master's include alone would have compiled cleanly and
silently deleted the documentation for every macOS build hint the port added --
the table is generated now, so a hint absent from the catalog has no
documentation anywhere.
So the 28 hints move into BuildHintsApple with the descriptions they had in the
table, and the generated table carries all 28 again. Confirmed by running
scripts/gen-build-hint-table.sh and counting them in the output, not by reading
the diff.
macNative.iosMinDeploymentTarget was already in the catalog and is NOT
duplicated; the first pass nearly added it back because a name pattern stopped
at the underscore in macos.add_libs and mis-parsed that row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Aug 27, 2026
master's #5586 made annotations the source for build hints that have one, and
the merge landed this port's 28 macos.* hints as hand-written catalog entries --
in HintGroup.MAC_NATIVE, whose key prefix is macNative. and which carries no
annotation at all. They belonged in the feature, not beside it.
HintGroup gains MAC_OS("Mac", "macos."), and @Mac declares all 28 as
compile-checked attributes: Toggle for the nine booleans, an appendable String[]
for addLibs following the ios.add_libs shape, and an explicit name for the
fifteen whose tail has dots and cannot be derived from a method name. The
hand-written entries are gone, because two sources for one hint is the drift
this feature exists to remove. macNative.* stays hand written: it is the
spelling the legacy Catalyst target reads and no annotation offers it.
Verified by regenerating rather than by reading the diff -- 28 macos rows in the
guide table, macos.entitlements.appSandbox typed boolean, and the catalog and
tools tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@shai-almog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Check build hints at compile time instead of shipping them inert - #5586

Merged
shai-almog merged 244 commits into
masterfrom
build-hint-annotations
Aug 27, 2026
Merged

Check build hints at compile time instead of shipping them inert#5586
shai-almog merged 244 commits into
masterfrom
build-hint-annotations

Conversation

@shai-almog

@shai-almogshai-almog commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

The problem

A build hint is a codename1.arg.<name>=<value> line that reaches a builder as request.getArg(name, default). Nothing checked the name, so a misspelling was accepted, copied into the build request, never read, and silently discarded — a green build with the setting simply not applied.

Our own agent reference had been shipping keys in exactly that state:

Documented in skill/references/build-hints.mdActually read by the builders
android.xPermissionsandroid.xpermissions (AndroidGradleBuilder.java:1206)
android.minSdkVersionandroid.min_sdk_version
android.sdkVersion(nothing — android.targetSDKVersion is the real one)

The change

87 hints can now be written as annotations on the application's main class, where javac does the checking: a misspelled name is an unknown symbol, a wrong value type is a type error, and a value outside a hint's supported set is an unknown enum constant.

@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)
@Build(nativeTheme = ThemeMode.MODERN)
@DesktopBuild(titleBar = DesktopTitleBar.NATIVE, width = 1280, height = 800)
@Ios(themeMode = ThemeMode.MODERN, newStorageLocation = Toggle.ON)
publicclassMyApplicationextendsLifecycle {
}

Seven annotations — @Ios, @Android, @DesktopBuild, @Build, @Hardening, @IosPrivacy, @OnDeviceDebug — and ten enums.

No attribute has a default that means anything

Every default is a marker for "nothing was said": Toggle.DEFAULT, "", {}, 0, or an enum's @HintUnset constant. An attribute left out is absent from the class file, so the processor emits nothing for it and the build decides, exactly as it does for a hint nobody wrote.

This is the reason there are no boolean attributes left. boolean appBundle() default false reads as "off unless you turn it on" while AndroidGradleBuilder defaults android.appBundle to true — and a copy of the server's answer compiled into every app already built cannot follow the server when it changes. Toggle is the three-state replacement, and a test refuses any hint attribute that declares a value-bearing default.

The builders are untouched.BuildHintAnnotationProcessor converts the annotations back into the same key/value pairs, and CN1BuildMojo merges them before the command-line overlay, the CN1Lib merges and both preflights — so a library still appends onto an annotation-supplied value and -D still wins. Simulator publishes them as system properties at startup so cn1:run sees hints that no longer live in the properties file.

The properties file is untouched too. It stays the way to set the long tail and the open-ended families such as android.permission.<NAME> that an annotation cannot express, with no new warnings or errors on that path. Declaring one hint both ways is a build error.

Where a hint is declared

Exactly one of two places:

  • CodenameOne/src/com/codename1/annotations/buildhints if it has an annotation. These are hand-written and are the source of truth for the hints they expose; BuildHintAnnotationReader reads them back with ASM rather than any file restating them.
  • maven/build-hint-catalog otherwise — dynamic families, build-service-only hints, the long tail.

The hint set used to be described in five places that had drifted apart: a prose table in the developer guide, a runtime scraper of that table in the Settings tool that guessed each type by string-matching the description, a fifteen-entry schema in the simulator, a fourteen-entry separator map in the plugin, and a hand-written agent reference. Only 147 of ~520 names appeared in more than one.

Nothing is generated into the tree

No generated file is committed. BuildHintCodeGenerator renders two views, both during a build:

  • cn1-build-hints.json for the two editors that are Codename One apps and so have no bytecode reader — the Settings tool and the simulator's hint editor. Each module that needs it renders it into its own target/classes (maven/javase, maven/codenameone-maven-plugin, scripts/settings/common). The catalog cannot render its own, because the generator lives in build-hint-tools, which depends on the catalog.
  • the developer guide's table, rendered when the guide is built.

Anything that can read bytecode reads the annotations directly and never touches the data file.

The guide's table goes from 208 rows to 570 with no prose lost, and gains Type, Default and Annotation columns it never had. An annotated hint's Default reads "set by the build" rather than a value, which is the honest statement of the paragraph above.

Enums are emitted only where the accepted set is demonstrable from the code that reads the hint — HardeningPreflight rejects an unknown harden.level, IOSDependencyManager throws on an unknown ios.dependencyManager, and GenerateDesktopAppWrapperMojo silently falls back to native on an unknown desktop.titleBar, which is precisely the failure this removes.

Scope: generated projects are deliberately not migrated here

Every project the archetype and the initializr produce is pinned to a released Codename One version — the initializr hardcodes 7.0.267 in GeneratorModel.CN1_PLUGIN_VERSION — and no released core carries com.codename1.annotations.buildhints. A generated project would import annotations that do not resolve and fail to compile before the user has written a line.

So the archetype's __mainName__.java and codenameone_settings.properties, and the initializr's common.zip and four source archives, are unchanged. They move to annotations in a follow-up once a release containing the package is out. scripts/skindesigner (7.0.255) stays on properties for the same reason, and cn1:migrate-build-hints refuses any project whose core lacks the package.

The in-repo tool projects that build against the snapshot from source are migrated: gamebuilder, video-builder, cn1playground, certificatewizard, guibuilder, fidelity-app, purchase-test-app, settings, hellocodenameone. protocol-e2e is not in that list: its only hint is codename1.arg.java.version, which has to stay in the properties file because the compiler needs it in order to compile the class that would otherwise declare it.

docs/demos is deliberately excluded: it is the developer guide's snippet project, full of intentionally incomplete fragments, and running the annotation processors over it fails by design.

Settings tool

It no longer scrapes the guide's AsciiDoc and guesses types; it reads the catalog. It also validates closed value domains, and refuses to edit a hint an annotation already owns — reading META-INF/codenameone/build-hints.properties and showing "Set by @Ios(themeMode) on the main class" — because writing a property for such a hint would create the duplicate declaration that fails the next build.

Its own tests had never run anywhere: both workflows that touched the module passed -Dmaven.test.skip=true, so every test written for the POM reader and the hint editor was dead weight. They now run in PR CI, 161 of them.

Gates

  • scripts/check-build-hint-catalog.sh fails when code reads a hint the catalog does not describe, and when our own docs or project templates name one that no builder reads. Its baseline is empty, so it is a hard gate rather than a ratchet. 497 hints read by the builders, all described.
  • scripts/gen-build-hint-annotations.sh --check asserts the render still succeeds and produces the full set. It is not a drift check — there is no committed copy to drift from — but a render that silently produced nothing would otherwise reach a user as an editor with no hints in it.
  • A JUnit suite checks the catalog's own consistency (attribute-name legality including the JLS 9.6.1 Object/Annotation method-name rule, enum domains, separators matching what LibraryHintMerger used to define), and build-hint-tools asserts the invariants that need the complete hint set, since that only exists on a classpath carrying the rendered data.
  • BytecodeComplianceMojo re-stamps the manifest after its in-place class rewrites, so the order of process-annotations against it stops mattering.
  • The developer-guide and website workflows list every input the table is rendered from — the annotations, the catalog, the renderer and the script — so a reworded attribute cannot change published documentation without running the AsciiDoc and Vale checks.

A build hint is a `codename1.arg.<name>=<value>` line that reaches a builder as
`request.getArg(name, default)`. Nothing checked the name, so a misspelling was
accepted, copied into the build request, never read, and silently discarded: a
green build with the setting simply not applied.
Our own agent reference had been shipping `android.xPermissions`,
`android.minSdkVersion` and `android.sdkVersion` for exactly that reason. The
builders read `android.xpermissions`, `android.min_sdk_version`, and nothing at
all.
Most hints can now be written as annotations on the application's main class,
where javac does the checking: a misspelled name is an unknown symbol, a wrong
value type is a type error, and a value outside a hint's supported set is an
unknown enum constant.
@iOS(newStorageLocation = true, themeMode = IosThemeMode.MODERN)
@android(minSdkVersion = 24, useAndroidX = true)
@desktop(titleBar = DesktopTitleBar.NATIVE)
public class MyApplication extends Lifecycle {
}
The builders are untouched: `BuildHintAnnotationProcessor` converts the
annotations back into the same key/value pairs and `CN1BuildMojo` merges them
before the command-line overlay, the CN1Lib merges and both preflights, so a
library still appends onto an annotation-supplied value and `-D` still wins.
`Simulator` publishes them as system properties at startup so `cn1:run` sees
hints that no longer live in the properties file.
The properties file is untouched too. It stays the way to set the long tail and
the open-ended families such as `android.permission.<NAME>` that an annotation
cannot express, with no new warnings or errors. Declaring one hint both ways is
a build error.
One catalog, five generated views
---------------------------------
The hint set had been described in five places that had drifted apart: a prose
table in the developer guide, a runtime scraper of that table in the Settings
tool that guessed each type by string-matching the description, a fifteen-entry
schema in the simulator, a fourteen-entry separator map in the plugin, and a
hand-written agent reference. Only 147 of ~520 names appeared in more than one.
`maven/build-hint-catalog` is now the single source of truth (529 hints: 457
mined from the builders, 56 documented-but-unread, 16 dynamic families; 82
exposed as annotation attributes). The annotations, the binding table the
processor reads back, the guide's table, the simulator's editor schema and the
agent reference are all generated from it. The guide's table goes from 208 rows
to 529 with no prose lost.
Enums are emitted only where the accepted set is demonstrable from the code that
reads the hint -- `HardeningPreflight` rejects an unknown `harden.level`,
`IOSDependencyManager` throws on an unknown `ios.dependencyManager`, and
`GenerateDesktopAppWrapperMojo` silently falls back to `native` on an unknown
`desktop.titleBar`, which is the failure this removes.
Generated projects
------------------
The archetype and all four initializr templates now carry the annotations, and
`cn1:migrate-build-hints` moves an existing project over. Eleven in-repo
projects are migrated. `java.version` deliberately stays in the properties file:
it picks the toolchain that compiles the class the annotations live on.
Gates
-----
`scripts/check-build-hint-catalog.sh` fails when code reads a hint the catalog
does not describe, and when our own docs or templates name one that no builder
reads. Its baseline is empty, so it is a hard gate rather than a ratchet.
`scripts/gen-build-hint-annotations.sh --check` fails on generated-file drift.
Both run in the Java 8 leg of PR CI.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:8d2cfcfde3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadmaven/pom.xml
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs[Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • build-hint-catalog: 0 findings (no issues)
    • build-hint-tools: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

`.gitignore` carries a repo-wide `**/build/*`. The catalog's package is
`com.codename1.build.shared`, so all 13 of its sources sat under a path segment
named `build` and `git add` silently skipped them. Only `pom.xml` was committed:
the module built locally from the working tree and produced an empty jar in CI,
which is why `codenameone-maven-plugin` then failed with `cannot find symbol` on
`BuildHints` and nearly every job went red.
The sibling `platform-feature-catalog` lives in the same package and is fine,
because it was added before that rule existed -- tracked files stay tracked, so
nothing ever pointed at the hazard.
Un-ignore `build` when it is a Java package rather than a build output
directory, with the rationale beside the rule so the next file added there is
not lost the same way. `maven/core/build/*` and `CodenameOne/build/*` stay
ignored.
Also from review:
- Every bare `open()` in the four Python scripts now uses a context manager, so
the handle closes even if parsing or `json.dump` raises, and the writes state
their encoding.
- The generator no longer emits an IP literal as an annotation default. PMD
reads `default "127.0.0.1"` as hardcoded configuration, and the default clause
is documentation only -- the processor emits a hint solely for members the
developer actually wrote -- so the value moves to the javadoc where it belongs.
- Files the migration touched that never carried a copyright header now have the
complete one. The archetype's `__mainName__.java` is excluded instead: it is a
template for the user's own application class, and stamping a Codename One GPL
header onto it would put our licence on their code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Pin generated projects to a version containing the annotations

The updated Initializr starter archives now import and use com.codename1.annotations.buildhints, but GeneratorModel.java:44 still generates projects pinned to CN1 7.0.267, whose core artifact predates this package. Consequently every newly generated barebones, Kotlin, Grub, or Tweet project fails compilation on the unresolved annotations unless the user manually changes the CN1 version; either defer these template changes or update the generated runtime/plugin version to the first release containing them.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The archived bootstrap ran generation at module scope, so gen_external.py's
`import gen_catalog` -- which only wants three helper functions -- rewrote every
catalog source as a side effect. Generation and its diagnostics now live in
`main()` behind a `__main__` guard, and the module-level file reads became
`load_license()` / `load_mined()` / `load_docs()`, so importing does no I/O and
cannot fail on inputs the archived copy deliberately does not carry.
Verified both directions: importing leaves the catalog untouched, and running
the two scripts end to end still reproduces the committed catalog byte for byte.
Also drops `json` and `subprocess` from check-build-hint-catalog.py. Both were
left from an earlier version that shelled out to the miner instead of importing
it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:d727c7d976

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadscripts/gen-build-hint-annotations.sh Outdated
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendscalar fallback (no native SIMD)
SIMD int-add (64K x300)java 196ms / native 73ms = 2.6x speedup
SIMD float-mul (64K x300)java 144ms / native 89ms = 1.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode83.000 ms
Base64 CN1 decode86.000 ms
Base64 native encode391.000 ms
Base64 encode ratio (CN1/native)0.212x (78.8% faster)
Base64 native decode310.000 ms
Base64 decode ratio (CN1/native)0.277x (72.3% faster)
Image encode benchmark statusskipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 60ms / native 4ms = 15.0x speedup
SIMD float-mul (64K x300)java 80ms / native 3ms = 26.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode190.000 ms
Base64 CN1 decode381.000 ms
Base64 SIMD encode99.000 ms
Base64 encode ratio (SIMD/CN1)0.521x (47.9% faster)
Base64 SIMD decode91.000 ms
Base64 decode ratio (SIMD/CN1)0.239x (76.1% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)22.000 ms
Image createMask (SIMD on)15.000 ms
Image createMask ratio (SIMD on/off)0.682x (31.8% faster)
Image applyMask (SIMD off)71.000 ms
Image applyMask (SIMD on)35.000 ms
Image applyMask ratio (SIMD on/off)0.493x (50.7% faster)
Image modifyAlpha (SIMD off)44.000 ms
Image modifyAlpha (SIMD on)22.000 ms
Image modifyAlpha ratio (SIMD on/off)0.500x (50.0% faster)
Image modifyAlpha removeColor (SIMD off)34.000 ms
Image modifyAlpha removeColor (SIMD on)25.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.735x (26.5% faster)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 63ms / native 5ms = 12.6x speedup
SIMD float-mul (64K x300)java 63ms / native 4ms = 15.7x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode327.000 ms
Base64 CN1 decode216.000 ms
Base64 SIMD encode176.000 ms
Base64 encode ratio (SIMD/CN1)0.538x (46.2% faster)
Base64 SIMD decode132.000 ms
Base64 decode ratio (SIMD/CN1)0.611x (38.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)28.000 ms
Image createMask (SIMD on)23.000 ms
Image createMask ratio (SIMD on/off)0.821x (17.9% faster)
Image applyMask (SIMD off)58.000 ms
Image applyMask (SIMD on)56.000 ms
Image applyMask ratio (SIMD on/off)0.966x (3.4% faster)
Image modifyAlpha (SIMD off)66.000 ms
Image modifyAlpha (SIMD on)28.000 ms
Image modifyAlpha ratio (SIMD on/off)0.424x (57.6% faster)
Image modifyAlpha removeColor (SIMD off)44.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.727x (27.3% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 55ms / native 3ms = 18.3x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode268.000 ms
Base64 CN1 decode152.000 ms
Base64 SIMD encode66.000 ms
Base64 encode ratio (SIMD/CN1)0.246x (75.4% faster)
Base64 SIMD decode64.000 ms
Base64 decode ratio (SIMD/CN1)0.421x (57.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)13.000 ms
Image createMask (SIMD on)9.000 ms
Image createMask ratio (SIMD on/off)0.692x (30.8% faster)
Image applyMask (SIMD off)24.000 ms
Image applyMask (SIMD on)20.000 ms
Image applyMask ratio (SIMD on/off)0.833x (16.7% faster)
Image modifyAlpha (SIMD off)17.000 ms
Image modifyAlpha (SIMD on)12.000 ms
Image modifyAlpha ratio (SIMD on/off)0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off)21.000 ms
Image modifyAlpha removeColor (SIMD on)13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.619x (38.1% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

Three separate gates rejected generated output. Each is fixed in the generator
so the class of problem cannot come back through a catalog edit.
Unmappable characters. The prose is imported from the developer guide, which
uses typographic punctuation, and `CodenameOne/src` is also compiled by an Ant
javac step with ASCII encoding where a single em dash is
`error: unmappable character for encoding ASCII` -- a build failure, not a
warning. A Unicode escape would not have helped: javac expands `\uXXXX` before
it strips comments, so the character reappears. `toAscii` now folds the
punctuation that actually occurs, and *refuses* anything it has no mapping for
rather than dropping it, because silently deleting a character from a hint's
documentation is the worse outcome.
Broken table. `ios.spm.packages` is documented as `identity|url|requirement`,
and a bare `|` starts a new AsciiDoc cell, so asciidoctor reported "dropping
cells from incomplete row" for the whole 529-row table. Cells are escaped now.
Vale. The guide enforces the Microsoft style as errors, and the generated table
feeds it, so the catalog's prose has to satisfy it too: contractions, no
"and so on", no stray adverbs. A default value is not prose, though -- the one
remaining hit was `android.file_paths`, whose default is an XML fragment -- so
a quoted default now carries the `// vale-skip:` comment .vale.ini documents
for individual false positives.
Also fixes a data bug the guide exposed. The miner preserved Java escape
sequences instead of decoding them, so `android.file_paths` and
`android.facebook_permissions` recorded defaults containing literal
backslashes that the build never sees, and those reached the rendered table.
The miner decodes escapes and re-quotes safely, and the two catalog entries are
corrected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/build_hint_miner.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:96bff9038a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

`killedThreadReportsItselfFinished` failed the Java 21 leg with
"FormTest timed out after 5000ms; edt=initialized pendingSerialCalls=0". The
waits in this class used a 5000ms deadline, which is exactly the `@FormTest`
timeout in EDTTestInterceptor -- so on a loaded runner the poll loop consumed
the entire harness budget and the interceptor fired first. The report then said
only that the method timed out, with nothing about which condition never became
true.
The waits now use 2000ms, well inside the harness budget and still roughly two
thousand times the ~1ms these threads actually take to stop. A genuine
regression now fails on the test's own assertion, which names what went wrong.
Pre-existing (the test arrived with #5526) and unrelated to the build hint work:
core-unittests has no dependency on the JavaSE port, so none of the simulator
registration in this branch runs there, this branch changes nothing under
com.codename1.db or EasyThread, and the Java 8 leg passed the same commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:a343fe3335

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

A mojo's defaultPhase does not bind it to a project -- the project's POM has to
-- and nothing turns a build hint annotation back into a codename1.arg.* pair
except the process-annotations goal. So migrating a project without that
binding deleted working properties and replaced them with annotations no goal
ever reads: the hints vanished from the build with no diagnostic anywhere.
Five projects in this branch were already in that state. gamebuilder,
docs/demos, video-builder and cn1playground bind the plugin but not that goal,
so the binding is added. input-validation-app's common module has no build
section at all, so its migration is reverted rather than inventing a lifecycle
for a demo app.
The goal now checks the reactor for the binding and refuses with the execution
block to paste, so this cannot happen to anyone else.
Three more from the same review:
- The deletion pass recognized only `key=value`. `Properties.load` also accepts
`key:value`, `key value`, escaped separators inside the key, and logical
continuation lines; a declaration it failed to match was left behind while the
annotation was added, so the next build failed with the duplicate-hint error
this goal exists to prevent. Keys are parsed the way Properties.load defines
them now, with a unit test per form.
- The settings file was read as ISO-8859-1 and written back as UTF-8, turning
any unrelated non-ASCII byte -- an accented displayName, say -- into mojibake.
It is written back as ISO-8859-1.
- cn1.androidTheme and cn1.nativeTheme are deprecated aliases of and.themeMode
and nativeTheme, which the builders honour as fallbacks. Neither declared
aliasOf, so conflict detection missed them and one value silently won.
Also: the generation script rebuilt the generator only when its class was
absent, so editing a catalog source and rerunning regenerated every view from
the previous build's bytecode -- reporting success while ignoring the edit, and
passing --check on a tree that was genuinely stale. It always rebuilds now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:0edef42ca4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…plicating a hint
docs/demos is the developer guide's snippet project: deliberately incomplete
code fragments that illustrate @entity, @route, @AppIntent and @mapped. Binding
process-annotations there put those snippets in front of the other processors,
which correctly rejected six of them, so the migration is reverted and its two
hints are back in the properties file. That the project omitted the goal was the
point, not an oversight.
The other three newly bound projects were checked rather than assumed:
gamebuilder, video-builder and cn1playground each run process-annotations
cleanly and emit 6, 3 and 5 hints respectively.
Settings could still create the duplicate the migration is careful to avoid. In
a generated project ios.themeMode and its neighbours are annotations, but the
Build Hints UI decides a hint is inactive from the properties file alone and its
Add button writes a property -- producing a second declaration that fails the
next build. The tool now reads META-INF/codenameone/build-hints.properties, the
file the processor writes on every build and deletes when the last annotation
goes, and renders those hints read-only with the attribute that owns them:
"Set by @iOS(themeMode) on the main class." An unbuilt project has no such file
and behaves as before.
Also fixes the SpotBugs finding this branch introduced: `backslashes % 2 == 1`
in the continuation scan is false for negative odd numbers, so it is `!= 0`.
The count cannot go negative, but the idiom is wrong regardless of that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Keep Initializr templates compatible with the pinned runtime

Every Initializr source archive now imports com.codename1.annotations.buildhints and uses the new annotations, while GeneratorModel.CN1_PLUGIN_VERSION still rewrites generated projects to 7.0.267, whose codenameone-core predates that package; the generated common POM also omits process-annotations. Consequently all newly downloaded Initializr projects fail compilation instead of receiving the defaults removed from common.zip's settings file. Leave these templates property-backed until Initializr targets the release containing this feature, or bump the generated version and bind the processor.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 339 seconds

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 62ms / native 3ms = 20.6x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathactive (NEON-accelerated)
Base64 CN1 encode167.000 ms
Base64 CN1 decode120.000 ms
Base64 native encode624.000 ms
Base64 encode ratio (CN1/native)0.268x (73.2% faster)
Base64 native decode231.000 ms
Base64 decode ratio (CN1/native)0.519x (48.1% faster)
Base64 SIMD encode56.000 ms
Base64 encode ratio (SIMD/CN1)0.335x (66.5% faster)
Base64 SIMD decode45.000 ms
Base64 decode ratio (SIMD/CN1)0.375x (62.5% faster)
Base64 encode ratio (SIMD/native)0.090x (91.0% faster)
Base64 decode ratio (SIMD/native)0.195x (80.5% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)6.000 ms
Image createMask (SIMD on)2.000 ms
Image createMask ratio (SIMD on/off)0.333x (66.7% faster)
Image applyMask (SIMD off)46.000 ms
Image applyMask (SIMD on)36.000 ms
Image applyMask ratio (SIMD on/off)0.783x (21.7% faster)
Image modifyAlpha (SIMD off)39.000 ms
Image modifyAlpha (SIMD on)39.000 ms
Image modifyAlpha ratio (SIMD on/off)1.000x (0.0% slower)
Image modifyAlpha removeColor (SIMD off)47.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.681x (31.9% faster)

Every project the archetype and the initializr produce is pinned to a released
Codename One version -- the initializr hardcodes 7.0.267 in
GeneratorModel.CN1_PLUGIN_VERSION -- and no released core carries
com.codename1.annotations.buildhints. So a generated project would import
annotations that do not resolve and fail to compile before the user has written
a line, and the settings those templates stopped declaring would simply be gone.
The templates are reverted to exactly their previous state: the archetype's
__mainName__.java and codenameone_settings.properties, and the initializr's
common.zip and four source archives. They can move to annotations in a follow-up
once a release containing the package is out.
The generated build hint table is dropped from the agent skill reference for the
same reason -- it documented a form those projects cannot use yet -- so the
generator no longer rewrites markdown at all.
What stays from that area is unrelated to annotations: the skill reference
described build hints that no builder reads, so a reader copying them got a
green build and no effect. android.xPermissions is spelled android.xpermissions,
android.minSdkVersion is android.min_sdk_version, and android.sdkVersion,
android.googlePlayVersion, build.compile, build.timeout, javascript.html5,
javascript.bundleResources and ios.orientation do not exist at all. Those
corrections are right for the published version too, and the catalog gate now
holds our own documentation to them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
compilesJava() already withheld the conventional src/main/java from the search
when the POM chain switches default-compile off with <phase>none</phase> and
binds nothing in its place. <sourceDirectory> is the same root, declared instead
of assumed, and it was not gated -- so a module that compiles nothing with javac
still offered it, and a stale copy of the main class there answered ahead of the
compiled source.
Per element rather than over the whole list, because a Kotlin-only module is
exactly the case where javac does not run and its <sourceDirs> must still be
searched. Both directions are asserted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:705783d9b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/release-on-maven-central.yml
shai-almogand others added 10 commits August 27, 2026 11:08
…writes
The .java branch in write() guarded against a non-ASCII character reaching a
generated Java source; the generator writes a JSON data file and an asciidoc
table and nothing else. toAscii survives because the reader test still needs it
-- the catalog's prose came from the developer guide and the annotations are
ASCII Java sources, so the two cannot be compared verbatim -- and its
documentation now says that rather than describing a build step it no longer
feeds.
Also the HintGroup local in Bindings that nothing read, and the no-op continue
at the end of the loop that was its only use. toHint already resolves the group
and throws when an annotation type names none.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Central recovery poll queried the two build hint artifacts with a single
curl carrying two URLs. curl needs an -o per URL and emits --write-out after
each transfer, so the second POM body arrived on stdout with both status codes
appended: the captured value could never equal 200, and the poll -- which exists
to rescue a release Central has already accepted -- would exhaust all 90
attempts and fail it. Reported by codex.
Both this poll and the R2 confirmation walk a list of artifacts, and each
carried its own copy, which had already drifted: Central's omitted
codenameone-core. The list is now named once in the job environment and both
read it, with a guard that refuses an empty list rather than reporting success
for having checked nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aliasOf, deprecated, external, enterpriseOnly and link exist so an annotated hint
can say what the catalog entries already say, and none of the 87 shipped
attributes needs any of them -- so the reader's handling of all five was carried
by no test at all. Exercised now against a copy of the real annotation package
with one probe attribute added, which is what the reader compiles anyway.
Kept rather than deleted: the first annotated hint that has to be marked
deprecated needs somewhere to say so, and would otherwise be the first thing ever
to run that path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
validateScreenshotContent threw before the write, so a failing scenario left
nothing behind but a pixel count -- which is exactly what happened to the
component-inspector run that reported textPixels=738 with no image to look at.
The capture is now written as <name>.png.rejected.png first, into the directory
the workflow already uploads with if: always(), so the next occurrence can be
diagnosed instead of guessed at.
Not a fix for that failure: the check reads a fixed screen rectangle and the run
before and after it were green on the same commit, so what it saw is still
unknown. This is what makes finding out possible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e</phase>
compilesJava() answered from the first level in the chain that switched
default-compile off, so an ancestor that disables it decided for a child that
binds its own compile execution. A module that plainly does compile Java read as
one that does not, and its Java roots were then withheld from the search -- where
a Kotlin or stale copy of the main class answers for it instead.
An enabled compile binding anywhere in the chain now settles it, and it is looked
for before any disabling level is allowed to decide. Extracted to a static form
taking the chain directly, because the walk needed a project on disk and was the
untested half of a method whose two halves are separately covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
attributeOf was written to replace a substring search for combine.self, and then
looked for the attribute name anywhere inside the start tag -- so xcombine.self
would have answered for it. The same rule, reproduced one level down in the fix
for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…were masking
Two failures on the JDK 8 leg, one the cause of the other.
The module's POM binds cn1:css at process-classes and the CSS compiler opens a
JFrame, which in this container is java.awt.HeadlessException before a single
test runs. Run under xvfb-run, which this job already uses for the Ant build a
few steps later.
That failure then skipped every step after it, including "Run SpotBugs for
ByteCodeTranslator" -- so the quality report failed for a missing
ByteCodeTranslator report, which said nothing about what had actually broken.
The step now runs last among this leg's gates, which also keeps its reinstall of
the maven plugin away from the SpotBugs reports the earlier steps produce.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three groups, none of which any reviewer asked for and all of which I added off
my own red-team pass:
The Settings POM-parser rewrites -- depth-matched </plugin>, combine.self and
combine.children read off their own element, comment stripping in parentPomPath,
the compilesJava chain walk, and the <sourceDirectory> gate. These change how the
Settings tool parses arbitrary user POMs, on the strength of cases nobody
reported. The parser is back to what it was.
The simulator verifier's rejected-capture diagnostics, which have nothing to do
with build hints -- I added them while chasing a screenshot failure that predates
this branch.
The release workflow's shared RELEASE_ARTIFACTS list. Only the defect codex
reported is kept: the two build hint POMs are queried with one curl each, because
two URLs share the single -o and the captured value could never be 200.
The plugin-side cleanups that rode along in the same commit as the parser work --
the unused ProcessorContext constructors and LibraryHintMerger's duplicate
prefix -- are kept, as is the comment de-duplication.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A generated file in git conflicts on every merge and there is nothing for a hand
edit to survive in, so it should never have been checked in.
It cannot simply move to one module's target/classes: BuildHints.entries() loads
it, so every consumer of the catalog needs it on its own classpath, and the
generator lives in build-hint-tools which depends on the catalog -- the catalog
cannot render its own resource without a dependency cycle. So each of the three
modules that actually needs it renders it into its own target/classes at
process-classes: maven/javase, maven/codenameone-maven-plugin and
scripts/settings/common. exec:java with classpathScope=compile, because a
plugin-level dependency resolves from the repository rather than the reactor and
would be a chicken and egg on a clean checkout. build-hint-tools is provided
scope in the two application modules so ASM cannot reach a built app.
Verified: all three render byte-identically to the file that was committed, the
catalog jar no longer carries it, and the javase jar does.
Consequences elsewhere. gen-build-hint-annotations.sh renders to a scratch
directory, and --check no longer diffs against a committed copy -- there is none
-- but asserts the rendering still succeeds and produces the full set, which is
the failure that would otherwise reach a user as an editor with no hints in it.
check-build-hint-catalog.py read the committed path and returned an empty set
when it was absent, which failed the gate OPEN; it now reads whichever module has
rendered it and says to build one when none has. The Ant JavaSE build takes it
from maven/javase/target/classes when that build has run, and without it the
simulator keeps the hints BuildHintSchemaDefaults compiles in and says the
annotated ones are missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removing the committed data file broke two catalog tests, and I did not see it
because I verified that module with -DskipTests -- on the one module whose
structure I had just changed.
BuildHints.entries() is complete only on a classpath carrying the rendered data
file. The catalog module cannot render one for its own tests: the generator lives
in build-hint-tools, which depends on the catalog. So the two assertions that
need the complete set -- every alias resolves to a real hint, and the catalog
agrees with LibraryHintMerger on every separator it defines -- move to
build-hint-tools, which renders the data into its own test classes first. Both
fail without that render, which is checked.
What stays beside the catalog is what its own sources declare, and the class now
says so rather than looking like it covers everything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:9b07db8e93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/developer-guide-docs.yml
shai-almogand others added 3 commits August 27, 2026 14:05
…lows
Making the table generated instead of committed took away the trigger it used to
get for free: while it was checked in, editing it showed up as a docs diff. The
replacement path list named the catalog and the render script but not the two
inputs that matter most -- the annotations, whose javadoc is the Description
column verbatim, and build-hint-tools, which is the renderer itself. A PR
renaming an attribute or rewording its documentation could therefore change the
guide without ever running the AsciiDoc and Vale checks over the result.
Reported by codex.
Fixed in both copies of that list in developer-guide-docs.yml, since triggering
the workflow is not enough on its own -- the HTML and PDF build is gated on the
paths-filter as well, and the two had already drifted.
And in website-docs.yml, which is the other workflow that renders the same table
and had the same gap for the same reason. Its pull_request and push lists both
already covered CodenameOne/src/**, so only the catalog, the renderer and the
script were missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two files overlapped and both auto-merged: website-docs.yml keeps master's
website telemetry step alongside the build hint table paths added here, and
CLAUDE.md keeps master's restructure -- 163 lines cut and the GC notes moved to
vm/CLAUDE.md -- alongside this branch's build hints section.
That section is rewritten rather than merged as-is: it still described writing
cn1-build-hints.json into the tree and told the reader to run the generator to
"rewrite the data file". Nothing is written into the tree any more, so it now
says where the file is rendered instead, and the command list loses the step that
no longer does anything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
windows-tooling builds the Settings tool with -Dcodename1.platform=javase, which
activates a profile declaring exec-maven-plugin with its own plugin level
<arguments> containing a <classpath/> element. Maven merges plugin level
configuration into every execution of that plugin, so that Object landed in the
generator execution's String array:
Cannot store value into array: ... can not cast one of the elements of
java.lang.Object[] to the type of the destination array, java.lang.String
A plain local build never activates that profile, which is why four green local
runs said nothing. Reproduced with the property set, fixed with
combine.self="override" on the arguments, and re-verified against the exact
command the workflow runs. Applied to all four generator executions rather than
only the one that failed: the collision needs a second declaration of the same
plugin anywhere in the effective build, which is not a property of this module.
Also restores the simulator verifier's rejected capture. It was reverted as out
of scope, and then the component inspector scenario failed a second time with a
byte identical textPixels=738 -- twice the same number is a state, not a race --
with no image kept to say what was on screen. Two failures in 22 runs on this
branch and none in 38 on any other is too specific to guess at.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:2d2708a0c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almogand others added 5 commits August 27, 2026 14:50
BytecodeComplianceMojo rewrites class files in place -- capping a class to the
supported version, and redirecting a call the runtime does not have. The build
hint manifest records the main class's own bytes, because the simulator has no
bytecode reader and can only compare the class file itself. If the main class is
one of the rewritten ones, a manifest written before that goal describes a class
that no longer exists on disk, and the simulator reads a manifest generated
moments earlier as stale and publishes none of the annotated hints. Reported by
codex.
Every pom in this repository happens to run process-annotations after this goal,
where the stamp is taken from the rewritten bytes anyway, so nothing is broken
today -- I checked all twelve. Nothing enforces that order though, and the
failure mode is silent: hints disappear, no error. Re-stamping here as well makes
the order stop mattering, since whichever of the two runs last leaves a manifest
describing the class that is actually there. It is a no-op when there is no
manifest, which is every project that declares its hints in the properties file.
The test asserts the hazard and the repair together: capping a version changes
the class, which invalidates a stamp taken before it, and re-stamping restores
the match without touching the hints. Removing the re-stamp fails it. What the
test does not cover is the one-line call from executeImpl, which needs a
MavenProject to reach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…wants
Both predate this branch without one. check-copyright-headers is scoped to the
PR's diff, so editing them is what brought them into scope -- the gate working as
designed, not a new defect. They take the Codename One GPLv2 + Classpath
Exception header their siblings in this package carry.
I ran that gate locally before the last push and it reported success while
checking nothing: with no --base it has no diff to scope to, and "0 file(s)
passed" was the tell I read straight past. Run with --base origin/master it
checks the same 128 files CI does, and passes. check-cast-semantics.sh takes
--baseline, a ratchet file rather than a git ref, so it is whole-repo and running
it bare is valid; copyright was the only gate here with that shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guide said the compiler-checked form existed and the generated table named
the annotation for each hint, but nothing anywhere showed what one looks like. A
reader had the Annotation column and no way to turn `@Ios(pods)` into code.
Adds a worked example and the exact properties lines it replaces, then explains
the three things about the syntax that are not guessable from the table: a list
hint takes a Java array and the build joins it with that hint's own separator, a
boolean hint takes Toggle rather than boolean so that leaving it out means the
build decides, and a hint with a closed value set takes an enum. Also says
plainly that the long tail and the open-ended families stay in the properties
file and that the two forms mix freely.
Every mapping in the example is checked against the generated data rather than
written from memory: each attribute resolves to the hint name claimed, each enum
constant to the wire value claimed, and ios.pods really does join with a comma.
The Java snippet compiles against the annotations, the guide's snippet validator
passes (inline blocks are refused there, so both snippets live in docs/demos),
Vale is clean, and the chapter renders without warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LanguageTool fails the developer guide build on a single match, and I had
hyphenated it in one sentence and not the other. Caught by running the gate
locally on the rendered chapter rather than by a CI cycle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A new .java file, so the diff-scoped gate checks it. I ran that gate with a base
earlier and it passed, then added this file and pushed without re-running it --
which is exactly what my own note about running diff-scoped gates AFTER the
commit is for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6fd1406078

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadPorts/JavaSE/src/com/codename1/impl/javase/Simulator.java
A project that adds @build or @DesktopBuild to its main class but never binds
cn1:process-annotations -- an upgraded or hand-written POM -- compiles and
launches fine, emits no manifest, and the simulator returned quietly and applied
none of the annotated hints. CN1BuildMojo refuses a device build for exactly this
case, so the simulator was the one place where the hints vanished with nothing
said and local behaviour diverged from device behaviour for the same project.
Reported by codex.
It now says so. A warning rather than a refusal: the simulator's job is to start,
and what it is missing are build settings, not something it cannot run without.
Detected by scanning the main class file's bytes for the annotations package,
because the simulator has no bytecode reader and an annotation's type is in the
constant pool as a descriptor. That is wider than reading the annotation table --
a main class that merely mentions the package matches too -- which is why this
warns and does not refuse.
Three cases are asserted: annotated with no manifest warns and names both the
class and the goal, an unannotated main class is silent, and a main class absent
from the classpath is silent. The first fails if the marker check is removed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:b0f5213ef4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import com.codename1.annotations.buildhints.Toggle;

// tag::buildHintAnnotations[]
@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the documentation snippet out of application bytecode

When docs/demos is built for a device through its reactor, this source is compiled into the common module even though codenameone_settings.properties names com.codenameone.developerguide.DemoCode as the main class. CN1BuildMojo.failOnMisplacedAnnotations() rejects live build-hint annotations on every non-main class, so BuildHintAnnotationSnippet makes that build fail before submission; binding process-annotations would instead fail during process-classes. Store the include outside the compiled source root or render it from a noncompiled snippet.

Useful? React with 👍 / 👎.

The developer guide's quality gate counts Vale alerts at suggestion level and
fails on any of them. I checked the chapter locally with --minAlertLevel=error,
which hides warnings, so two Microsoft.Adverbs warnings -- "deliberately" and
"freely" -- went through and reddened the build. Both sentences say the same
thing without the adverb.
Re-checked at the level CI uses: Vale 0/0/0, LanguageTool 0 matches, snippet
validator 706 blocks, paragraph capitalization clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog
shai-almog merged commit d21a389 into masterAug 27, 2026
72 checks passed
@shai-almog
shai-almog deleted the build-hint-annotations branch August 27, 2026 17:13
shai-almog added a commit that referenced this pull request Aug 27, 2026
Two conflicts. .gitignore had a new entry on each side and keeps both.
Advanced-Topics-Under-The-Hood.asciidoc is the one that mattered: master's #5586
replaced the hand-written build hint table with an include generated from
maven/build-hint-catalog, while this branch had added 28 macos.* rows to that
table. Resolving to master's include alone would have compiled cleanly and
silently deleted the documentation for every macOS build hint the port added --
the table is generated now, so a hint absent from the catalog has no
documentation anywhere.
So the 28 hints move into BuildHintsApple with the descriptions they had in the
table, and the generated table carries all 28 again. Confirmed by running
scripts/gen-build-hint-table.sh and counting them in the output, not by reading
the diff.
macNative.iosMinDeploymentTarget was already in the catalog and is NOT
duplicated; the first pass nearly added it back because a name pattern stopped
at the underscore in macos.add_libs and mis-parsed that row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Aug 27, 2026
master's #5586 made annotations the source for build hints that have one, and
the merge landed this port's 28 macos.* hints as hand-written catalog entries --
in HintGroup.MAC_NATIVE, whose key prefix is macNative. and which carries no
annotation at all. They belonged in the feature, not beside it.
HintGroup gains MAC_OS("Mac", "macos."), and @Mac declares all 28 as
compile-checked attributes: Toggle for the nine booleans, an appendable String[]
for addLibs following the ios.add_libs shape, and an explicit name for the
fifteen whose tail has dots and cannot be derived from a method name. The
hand-written entries are gone, because two sources for one hint is the drift
this feature exists to remove. macNative.* stays hand written: it is the
spelling the legacy Catalyst target reads and no annotation offers it.
Verified by regenerating rather than by reading the diff -- 28 macos rows in the
guide table, macos.entitlements.appSandbox typed boolean, and the catalog and
tools tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@shai-almog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Check build hints at compile time instead of shipping them inert - #5586

Merged
shai-almog merged 244 commits into
masterfrom
build-hint-annotations
Aug 27, 2026
Merged

Check build hints at compile time instead of shipping them inert#5586
shai-almog merged 244 commits into
masterfrom
build-hint-annotations

Conversation

@shai-almog

@shai-almogshai-almog commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

The problem

A build hint is a codename1.arg.<name>=<value> line that reaches a builder as request.getArg(name, default). Nothing checked the name, so a misspelling was accepted, copied into the build request, never read, and silently discarded — a green build with the setting simply not applied.

Our own agent reference had been shipping keys in exactly that state:

Documented in skill/references/build-hints.mdActually read by the builders
android.xPermissionsandroid.xpermissions (AndroidGradleBuilder.java:1206)
android.minSdkVersionandroid.min_sdk_version
android.sdkVersion(nothing — android.targetSDKVersion is the real one)

The change

87 hints can now be written as annotations on the application's main class, where javac does the checking: a misspelled name is an unknown symbol, a wrong value type is a type error, and a value outside a hint's supported set is an unknown enum constant.

@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)
@Build(nativeTheme = ThemeMode.MODERN)
@DesktopBuild(titleBar = DesktopTitleBar.NATIVE, width = 1280, height = 800)
@Ios(themeMode = ThemeMode.MODERN, newStorageLocation = Toggle.ON)
publicclassMyApplicationextendsLifecycle {
}

Seven annotations — @Ios, @Android, @DesktopBuild, @Build, @Hardening, @IosPrivacy, @OnDeviceDebug — and ten enums.

No attribute has a default that means anything

Every default is a marker for "nothing was said": Toggle.DEFAULT, "", {}, 0, or an enum's @HintUnset constant. An attribute left out is absent from the class file, so the processor emits nothing for it and the build decides, exactly as it does for a hint nobody wrote.

This is the reason there are no boolean attributes left. boolean appBundle() default false reads as "off unless you turn it on" while AndroidGradleBuilder defaults android.appBundle to true — and a copy of the server's answer compiled into every app already built cannot follow the server when it changes. Toggle is the three-state replacement, and a test refuses any hint attribute that declares a value-bearing default.

The builders are untouched.BuildHintAnnotationProcessor converts the annotations back into the same key/value pairs, and CN1BuildMojo merges them before the command-line overlay, the CN1Lib merges and both preflights — so a library still appends onto an annotation-supplied value and -D still wins. Simulator publishes them as system properties at startup so cn1:run sees hints that no longer live in the properties file.

The properties file is untouched too. It stays the way to set the long tail and the open-ended families such as android.permission.<NAME> that an annotation cannot express, with no new warnings or errors on that path. Declaring one hint both ways is a build error.

Where a hint is declared

Exactly one of two places:

  • CodenameOne/src/com/codename1/annotations/buildhints if it has an annotation. These are hand-written and are the source of truth for the hints they expose; BuildHintAnnotationReader reads them back with ASM rather than any file restating them.
  • maven/build-hint-catalog otherwise — dynamic families, build-service-only hints, the long tail.

The hint set used to be described in five places that had drifted apart: a prose table in the developer guide, a runtime scraper of that table in the Settings tool that guessed each type by string-matching the description, a fifteen-entry schema in the simulator, a fourteen-entry separator map in the plugin, and a hand-written agent reference. Only 147 of ~520 names appeared in more than one.

Nothing is generated into the tree

No generated file is committed. BuildHintCodeGenerator renders two views, both during a build:

  • cn1-build-hints.json for the two editors that are Codename One apps and so have no bytecode reader — the Settings tool and the simulator's hint editor. Each module that needs it renders it into its own target/classes (maven/javase, maven/codenameone-maven-plugin, scripts/settings/common). The catalog cannot render its own, because the generator lives in build-hint-tools, which depends on the catalog.
  • the developer guide's table, rendered when the guide is built.

Anything that can read bytecode reads the annotations directly and never touches the data file.

The guide's table goes from 208 rows to 570 with no prose lost, and gains Type, Default and Annotation columns it never had. An annotated hint's Default reads "set by the build" rather than a value, which is the honest statement of the paragraph above.

Enums are emitted only where the accepted set is demonstrable from the code that reads the hint — HardeningPreflight rejects an unknown harden.level, IOSDependencyManager throws on an unknown ios.dependencyManager, and GenerateDesktopAppWrapperMojo silently falls back to native on an unknown desktop.titleBar, which is precisely the failure this removes.

Scope: generated projects are deliberately not migrated here

Every project the archetype and the initializr produce is pinned to a released Codename One version — the initializr hardcodes 7.0.267 in GeneratorModel.CN1_PLUGIN_VERSION — and no released core carries com.codename1.annotations.buildhints. A generated project would import annotations that do not resolve and fail to compile before the user has written a line.

So the archetype's __mainName__.java and codenameone_settings.properties, and the initializr's common.zip and four source archives, are unchanged. They move to annotations in a follow-up once a release containing the package is out. scripts/skindesigner (7.0.255) stays on properties for the same reason, and cn1:migrate-build-hints refuses any project whose core lacks the package.

The in-repo tool projects that build against the snapshot from source are migrated: gamebuilder, video-builder, cn1playground, certificatewizard, guibuilder, fidelity-app, purchase-test-app, settings, hellocodenameone. protocol-e2e is not in that list: its only hint is codename1.arg.java.version, which has to stay in the properties file because the compiler needs it in order to compile the class that would otherwise declare it.

docs/demos is deliberately excluded: it is the developer guide's snippet project, full of intentionally incomplete fragments, and running the annotation processors over it fails by design.

Settings tool

It no longer scrapes the guide's AsciiDoc and guesses types; it reads the catalog. It also validates closed value domains, and refuses to edit a hint an annotation already owns — reading META-INF/codenameone/build-hints.properties and showing "Set by @Ios(themeMode) on the main class" — because writing a property for such a hint would create the duplicate declaration that fails the next build.

Its own tests had never run anywhere: both workflows that touched the module passed -Dmaven.test.skip=true, so every test written for the POM reader and the hint editor was dead weight. They now run in PR CI, 161 of them.

Gates

  • scripts/check-build-hint-catalog.sh fails when code reads a hint the catalog does not describe, and when our own docs or project templates name one that no builder reads. Its baseline is empty, so it is a hard gate rather than a ratchet. 497 hints read by the builders, all described.
  • scripts/gen-build-hint-annotations.sh --check asserts the render still succeeds and produces the full set. It is not a drift check — there is no committed copy to drift from — but a render that silently produced nothing would otherwise reach a user as an editor with no hints in it.
  • A JUnit suite checks the catalog's own consistency (attribute-name legality including the JLS 9.6.1 Object/Annotation method-name rule, enum domains, separators matching what LibraryHintMerger used to define), and build-hint-tools asserts the invariants that need the complete hint set, since that only exists on a classpath carrying the rendered data.
  • BytecodeComplianceMojo re-stamps the manifest after its in-place class rewrites, so the order of process-annotations against it stops mattering.
  • The developer-guide and website workflows list every input the table is rendered from — the annotations, the catalog, the renderer and the script — so a reworded attribute cannot change published documentation without running the AsciiDoc and Vale checks.

A build hint is a `codename1.arg.<name>=<value>` line that reaches a builder as
`request.getArg(name, default)`. Nothing checked the name, so a misspelling was
accepted, copied into the build request, never read, and silently discarded: a
green build with the setting simply not applied.
Our own agent reference had been shipping `android.xPermissions`,
`android.minSdkVersion` and `android.sdkVersion` for exactly that reason. The
builders read `android.xpermissions`, `android.min_sdk_version`, and nothing at
all.
Most hints can now be written as annotations on the application's main class,
where javac does the checking: a misspelled name is an unknown symbol, a wrong
value type is a type error, and a value outside a hint's supported set is an
unknown enum constant.
@iOS(newStorageLocation = true, themeMode = IosThemeMode.MODERN)
@android(minSdkVersion = 24, useAndroidX = true)
@desktop(titleBar = DesktopTitleBar.NATIVE)
public class MyApplication extends Lifecycle {
}
The builders are untouched: `BuildHintAnnotationProcessor` converts the
annotations back into the same key/value pairs and `CN1BuildMojo` merges them
before the command-line overlay, the CN1Lib merges and both preflights, so a
library still appends onto an annotation-supplied value and `-D` still wins.
`Simulator` publishes them as system properties at startup so `cn1:run` sees
hints that no longer live in the properties file.
The properties file is untouched too. It stays the way to set the long tail and
the open-ended families such as `android.permission.<NAME>` that an annotation
cannot express, with no new warnings or errors. Declaring one hint both ways is
a build error.
One catalog, five generated views
---------------------------------
The hint set had been described in five places that had drifted apart: a prose
table in the developer guide, a runtime scraper of that table in the Settings
tool that guessed each type by string-matching the description, a fifteen-entry
schema in the simulator, a fourteen-entry separator map in the plugin, and a
hand-written agent reference. Only 147 of ~520 names appeared in more than one.
`maven/build-hint-catalog` is now the single source of truth (529 hints: 457
mined from the builders, 56 documented-but-unread, 16 dynamic families; 82
exposed as annotation attributes). The annotations, the binding table the
processor reads back, the guide's table, the simulator's editor schema and the
agent reference are all generated from it. The guide's table goes from 208 rows
to 529 with no prose lost.
Enums are emitted only where the accepted set is demonstrable from the code that
reads the hint -- `HardeningPreflight` rejects an unknown `harden.level`,
`IOSDependencyManager` throws on an unknown `ios.dependencyManager`, and
`GenerateDesktopAppWrapperMojo` silently falls back to `native` on an unknown
`desktop.titleBar`, which is the failure this removes.
Generated projects
------------------
The archetype and all four initializr templates now carry the annotations, and
`cn1:migrate-build-hints` moves an existing project over. Eleven in-repo
projects are migrated. `java.version` deliberately stays in the properties file:
it picks the toolchain that compiles the class the annotations live on.
Gates
-----
`scripts/check-build-hint-catalog.sh` fails when code reads a hint the catalog
does not describe, and when our own docs or templates name one that no builder
reads. Its baseline is empty, so it is a hard gate rather than a ratchet.
`scripts/gen-build-hint-annotations.sh --check` fails on generated-file drift.
Both run in the Java 8 leg of PR CI.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:8d2cfcfde3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadmaven/pom.xml
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs[Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • build-hint-catalog: 0 findings (no issues)
    • build-hint-tools: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

`.gitignore` carries a repo-wide `**/build/*`. The catalog's package is
`com.codename1.build.shared`, so all 13 of its sources sat under a path segment
named `build` and `git add` silently skipped them. Only `pom.xml` was committed:
the module built locally from the working tree and produced an empty jar in CI,
which is why `codenameone-maven-plugin` then failed with `cannot find symbol` on
`BuildHints` and nearly every job went red.
The sibling `platform-feature-catalog` lives in the same package and is fine,
because it was added before that rule existed -- tracked files stay tracked, so
nothing ever pointed at the hazard.
Un-ignore `build` when it is a Java package rather than a build output
directory, with the rationale beside the rule so the next file added there is
not lost the same way. `maven/core/build/*` and `CodenameOne/build/*` stay
ignored.
Also from review:
- Every bare `open()` in the four Python scripts now uses a context manager, so
the handle closes even if parsing or `json.dump` raises, and the writes state
their encoding.
- The generator no longer emits an IP literal as an annotation default. PMD
reads `default "127.0.0.1"` as hardcoded configuration, and the default clause
is documentation only -- the processor emits a hint solely for members the
developer actually wrote -- so the value moves to the javadoc where it belongs.
- Files the migration touched that never carried a copyright header now have the
complete one. The archetype's `__mainName__.java` is excluded instead: it is a
template for the user's own application class, and stamping a Codename One GPL
header onto it would put our licence on their code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Pin generated projects to a version containing the annotations

The updated Initializr starter archives now import and use com.codename1.annotations.buildhints, but GeneratorModel.java:44 still generates projects pinned to CN1 7.0.267, whose core artifact predates this package. Consequently every newly generated barebones, Kotlin, Grub, or Tweet project fails compilation on the unresolved annotations unless the user manually changes the CN1 version; either defer these template changes or update the generated runtime/plugin version to the first release containing them.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The archived bootstrap ran generation at module scope, so gen_external.py's
`import gen_catalog` -- which only wants three helper functions -- rewrote every
catalog source as a side effect. Generation and its diagnostics now live in
`main()` behind a `__main__` guard, and the module-level file reads became
`load_license()` / `load_mined()` / `load_docs()`, so importing does no I/O and
cannot fail on inputs the archived copy deliberately does not carry.
Verified both directions: importing leaves the catalog untouched, and running
the two scripts end to end still reproduces the committed catalog byte for byte.
Also drops `json` and `subprocess` from check-build-hint-catalog.py. Both were
left from an earlier version that shelled out to the miner instead of importing
it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:d727c7d976

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadscripts/gen-build-hint-annotations.sh Outdated
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendscalar fallback (no native SIMD)
SIMD int-add (64K x300)java 196ms / native 73ms = 2.6x speedup
SIMD float-mul (64K x300)java 144ms / native 89ms = 1.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode83.000 ms
Base64 CN1 decode86.000 ms
Base64 native encode391.000 ms
Base64 encode ratio (CN1/native)0.212x (78.8% faster)
Base64 native decode310.000 ms
Base64 decode ratio (CN1/native)0.277x (72.3% faster)
Image encode benchmark statusskipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 60ms / native 4ms = 15.0x speedup
SIMD float-mul (64K x300)java 80ms / native 3ms = 26.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode190.000 ms
Base64 CN1 decode381.000 ms
Base64 SIMD encode99.000 ms
Base64 encode ratio (SIMD/CN1)0.521x (47.9% faster)
Base64 SIMD decode91.000 ms
Base64 decode ratio (SIMD/CN1)0.239x (76.1% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)22.000 ms
Image createMask (SIMD on)15.000 ms
Image createMask ratio (SIMD on/off)0.682x (31.8% faster)
Image applyMask (SIMD off)71.000 ms
Image applyMask (SIMD on)35.000 ms
Image applyMask ratio (SIMD on/off)0.493x (50.7% faster)
Image modifyAlpha (SIMD off)44.000 ms
Image modifyAlpha (SIMD on)22.000 ms
Image modifyAlpha ratio (SIMD on/off)0.500x (50.0% faster)
Image modifyAlpha removeColor (SIMD off)34.000 ms
Image modifyAlpha removeColor (SIMD on)25.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.735x (26.5% faster)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 63ms / native 5ms = 12.6x speedup
SIMD float-mul (64K x300)java 63ms / native 4ms = 15.7x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode327.000 ms
Base64 CN1 decode216.000 ms
Base64 SIMD encode176.000 ms
Base64 encode ratio (SIMD/CN1)0.538x (46.2% faster)
Base64 SIMD decode132.000 ms
Base64 decode ratio (SIMD/CN1)0.611x (38.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)28.000 ms
Image createMask (SIMD on)23.000 ms
Image createMask ratio (SIMD on/off)0.821x (17.9% faster)
Image applyMask (SIMD off)58.000 ms
Image applyMask (SIMD on)56.000 ms
Image applyMask ratio (SIMD on/off)0.966x (3.4% faster)
Image modifyAlpha (SIMD off)66.000 ms
Image modifyAlpha (SIMD on)28.000 ms
Image modifyAlpha ratio (SIMD on/off)0.424x (57.6% faster)
Image modifyAlpha removeColor (SIMD off)44.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.727x (27.3% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 55ms / native 3ms = 18.3x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode268.000 ms
Base64 CN1 decode152.000 ms
Base64 SIMD encode66.000 ms
Base64 encode ratio (SIMD/CN1)0.246x (75.4% faster)
Base64 SIMD decode64.000 ms
Base64 decode ratio (SIMD/CN1)0.421x (57.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)13.000 ms
Image createMask (SIMD on)9.000 ms
Image createMask ratio (SIMD on/off)0.692x (30.8% faster)
Image applyMask (SIMD off)24.000 ms
Image applyMask (SIMD on)20.000 ms
Image applyMask ratio (SIMD on/off)0.833x (16.7% faster)
Image modifyAlpha (SIMD off)17.000 ms
Image modifyAlpha (SIMD on)12.000 ms
Image modifyAlpha ratio (SIMD on/off)0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off)21.000 ms
Image modifyAlpha removeColor (SIMD on)13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.619x (38.1% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

Three separate gates rejected generated output. Each is fixed in the generator
so the class of problem cannot come back through a catalog edit.
Unmappable characters. The prose is imported from the developer guide, which
uses typographic punctuation, and `CodenameOne/src` is also compiled by an Ant
javac step with ASCII encoding where a single em dash is
`error: unmappable character for encoding ASCII` -- a build failure, not a
warning. A Unicode escape would not have helped: javac expands `\uXXXX` before
it strips comments, so the character reappears. `toAscii` now folds the
punctuation that actually occurs, and *refuses* anything it has no mapping for
rather than dropping it, because silently deleting a character from a hint's
documentation is the worse outcome.
Broken table. `ios.spm.packages` is documented as `identity|url|requirement`,
and a bare `|` starts a new AsciiDoc cell, so asciidoctor reported "dropping
cells from incomplete row" for the whole 529-row table. Cells are escaped now.
Vale. The guide enforces the Microsoft style as errors, and the generated table
feeds it, so the catalog's prose has to satisfy it too: contractions, no
"and so on", no stray adverbs. A default value is not prose, though -- the one
remaining hit was `android.file_paths`, whose default is an XML fragment -- so
a quoted default now carries the `// vale-skip:` comment .vale.ini documents
for individual false positives.
Also fixes a data bug the guide exposed. The miner preserved Java escape
sequences instead of decoding them, so `android.file_paths` and
`android.facebook_permissions` recorded defaults containing literal
backslashes that the build never sees, and those reached the rendered table.
The miner decodes escapes and re-quotes safely, and the two catalog entries are
corrected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/build_hint_miner.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:96bff9038a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

`killedThreadReportsItselfFinished` failed the Java 21 leg with
"FormTest timed out after 5000ms; edt=initialized pendingSerialCalls=0". The
waits in this class used a 5000ms deadline, which is exactly the `@FormTest`
timeout in EDTTestInterceptor -- so on a loaded runner the poll loop consumed
the entire harness budget and the interceptor fired first. The report then said
only that the method timed out, with nothing about which condition never became
true.
The waits now use 2000ms, well inside the harness budget and still roughly two
thousand times the ~1ms these threads actually take to stop. A genuine
regression now fails on the test's own assertion, which names what went wrong.
Pre-existing (the test arrived with #5526) and unrelated to the build hint work:
core-unittests has no dependency on the JavaSE port, so none of the simulator
registration in this branch runs there, this branch changes nothing under
com.codename1.db or EasyThread, and the Java 8 leg passed the same commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:a343fe3335

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

A mojo's defaultPhase does not bind it to a project -- the project's POM has to
-- and nothing turns a build hint annotation back into a codename1.arg.* pair
except the process-annotations goal. So migrating a project without that
binding deleted working properties and replaced them with annotations no goal
ever reads: the hints vanished from the build with no diagnostic anywhere.
Five projects in this branch were already in that state. gamebuilder,
docs/demos, video-builder and cn1playground bind the plugin but not that goal,
so the binding is added. input-validation-app's common module has no build
section at all, so its migration is reverted rather than inventing a lifecycle
for a demo app.
The goal now checks the reactor for the binding and refuses with the execution
block to paste, so this cannot happen to anyone else.
Three more from the same review:
- The deletion pass recognized only `key=value`. `Properties.load` also accepts
`key:value`, `key value`, escaped separators inside the key, and logical
continuation lines; a declaration it failed to match was left behind while the
annotation was added, so the next build failed with the duplicate-hint error
this goal exists to prevent. Keys are parsed the way Properties.load defines
them now, with a unit test per form.
- The settings file was read as ISO-8859-1 and written back as UTF-8, turning
any unrelated non-ASCII byte -- an accented displayName, say -- into mojibake.
It is written back as ISO-8859-1.
- cn1.androidTheme and cn1.nativeTheme are deprecated aliases of and.themeMode
and nativeTheme, which the builders honour as fallbacks. Neither declared
aliasOf, so conflict detection missed them and one value silently won.
Also: the generation script rebuilt the generator only when its class was
absent, so editing a catalog source and rerunning regenerated every view from
the previous build's bytecode -- reporting success while ignoring the edit, and
passing --check on a tree that was genuinely stale. It always rebuilds now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:0edef42ca4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…plicating a hint
docs/demos is the developer guide's snippet project: deliberately incomplete
code fragments that illustrate @entity, @route, @AppIntent and @mapped. Binding
process-annotations there put those snippets in front of the other processors,
which correctly rejected six of them, so the migration is reverted and its two
hints are back in the properties file. That the project omitted the goal was the
point, not an oversight.
The other three newly bound projects were checked rather than assumed:
gamebuilder, video-builder and cn1playground each run process-annotations
cleanly and emit 6, 3 and 5 hints respectively.
Settings could still create the duplicate the migration is careful to avoid. In
a generated project ios.themeMode and its neighbours are annotations, but the
Build Hints UI decides a hint is inactive from the properties file alone and its
Add button writes a property -- producing a second declaration that fails the
next build. The tool now reads META-INF/codenameone/build-hints.properties, the
file the processor writes on every build and deletes when the last annotation
goes, and renders those hints read-only with the attribute that owns them:
"Set by @iOS(themeMode) on the main class." An unbuilt project has no such file
and behaves as before.
Also fixes the SpotBugs finding this branch introduced: `backslashes % 2 == 1`
in the continuation scan is false for negative odd numbers, so it is `!= 0`.
The count cannot go negative, but the idiom is wrong regardless of that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Keep Initializr templates compatible with the pinned runtime

Every Initializr source archive now imports com.codename1.annotations.buildhints and uses the new annotations, while GeneratorModel.CN1_PLUGIN_VERSION still rewrites generated projects to 7.0.267, whose codenameone-core predates that package; the generated common POM also omits process-annotations. Consequently all newly downloaded Initializr projects fail compilation instead of receiving the defaults removed from common.zip's settings file. Leave these templates property-backed until Initializr targets the release containing this feature, or bump the generated version and bind the processor.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 339 seconds

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 62ms / native 3ms = 20.6x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathactive (NEON-accelerated)
Base64 CN1 encode167.000 ms
Base64 CN1 decode120.000 ms
Base64 native encode624.000 ms
Base64 encode ratio (CN1/native)0.268x (73.2% faster)
Base64 native decode231.000 ms
Base64 decode ratio (CN1/native)0.519x (48.1% faster)
Base64 SIMD encode56.000 ms
Base64 encode ratio (SIMD/CN1)0.335x (66.5% faster)
Base64 SIMD decode45.000 ms
Base64 decode ratio (SIMD/CN1)0.375x (62.5% faster)
Base64 encode ratio (SIMD/native)0.090x (91.0% faster)
Base64 decode ratio (SIMD/native)0.195x (80.5% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)6.000 ms
Image createMask (SIMD on)2.000 ms
Image createMask ratio (SIMD on/off)0.333x (66.7% faster)
Image applyMask (SIMD off)46.000 ms
Image applyMask (SIMD on)36.000 ms
Image applyMask ratio (SIMD on/off)0.783x (21.7% faster)
Image modifyAlpha (SIMD off)39.000 ms
Image modifyAlpha (SIMD on)39.000 ms
Image modifyAlpha ratio (SIMD on/off)1.000x (0.0% slower)
Image modifyAlpha removeColor (SIMD off)47.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.681x (31.9% faster)

Every project the archetype and the initializr produce is pinned to a released
Codename One version -- the initializr hardcodes 7.0.267 in
GeneratorModel.CN1_PLUGIN_VERSION -- and no released core carries
com.codename1.annotations.buildhints. So a generated project would import
annotations that do not resolve and fail to compile before the user has written
a line, and the settings those templates stopped declaring would simply be gone.
The templates are reverted to exactly their previous state: the archetype's
__mainName__.java and codenameone_settings.properties, and the initializr's
common.zip and four source archives. They can move to annotations in a follow-up
once a release containing the package is out.
The generated build hint table is dropped from the agent skill reference for the
same reason -- it documented a form those projects cannot use yet -- so the
generator no longer rewrites markdown at all.
What stays from that area is unrelated to annotations: the skill reference
described build hints that no builder reads, so a reader copying them got a
green build and no effect. android.xPermissions is spelled android.xpermissions,
android.minSdkVersion is android.min_sdk_version, and android.sdkVersion,
android.googlePlayVersion, build.compile, build.timeout, javascript.html5,
javascript.bundleResources and ios.orientation do not exist at all. Those
corrections are right for the published version too, and the catalog gate now
holds our own documentation to them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
compilesJava() already withheld the conventional src/main/java from the search
when the POM chain switches default-compile off with <phase>none</phase> and
binds nothing in its place. <sourceDirectory> is the same root, declared instead
of assumed, and it was not gated -- so a module that compiles nothing with javac
still offered it, and a stale copy of the main class there answered ahead of the
compiled source.
Per element rather than over the whole list, because a Kotlin-only module is
exactly the case where javac does not run and its <sourceDirs> must still be
searched. Both directions are asserted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:705783d9b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/release-on-maven-central.yml
shai-almogand others added 10 commits August 27, 2026 11:08
…writes
The .java branch in write() guarded against a non-ASCII character reaching a
generated Java source; the generator writes a JSON data file and an asciidoc
table and nothing else. toAscii survives because the reader test still needs it
-- the catalog's prose came from the developer guide and the annotations are
ASCII Java sources, so the two cannot be compared verbatim -- and its
documentation now says that rather than describing a build step it no longer
feeds.
Also the HintGroup local in Bindings that nothing read, and the no-op continue
at the end of the loop that was its only use. toHint already resolves the group
and throws when an annotation type names none.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Central recovery poll queried the two build hint artifacts with a single
curl carrying two URLs. curl needs an -o per URL and emits --write-out after
each transfer, so the second POM body arrived on stdout with both status codes
appended: the captured value could never equal 200, and the poll -- which exists
to rescue a release Central has already accepted -- would exhaust all 90
attempts and fail it. Reported by codex.
Both this poll and the R2 confirmation walk a list of artifacts, and each
carried its own copy, which had already drifted: Central's omitted
codenameone-core. The list is now named once in the job environment and both
read it, with a guard that refuses an empty list rather than reporting success
for having checked nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aliasOf, deprecated, external, enterpriseOnly and link exist so an annotated hint
can say what the catalog entries already say, and none of the 87 shipped
attributes needs any of them -- so the reader's handling of all five was carried
by no test at all. Exercised now against a copy of the real annotation package
with one probe attribute added, which is what the reader compiles anyway.
Kept rather than deleted: the first annotated hint that has to be marked
deprecated needs somewhere to say so, and would otherwise be the first thing ever
to run that path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
validateScreenshotContent threw before the write, so a failing scenario left
nothing behind but a pixel count -- which is exactly what happened to the
component-inspector run that reported textPixels=738 with no image to look at.
The capture is now written as <name>.png.rejected.png first, into the directory
the workflow already uploads with if: always(), so the next occurrence can be
diagnosed instead of guessed at.
Not a fix for that failure: the check reads a fixed screen rectangle and the run
before and after it were green on the same commit, so what it saw is still
unknown. This is what makes finding out possible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e</phase>
compilesJava() answered from the first level in the chain that switched
default-compile off, so an ancestor that disables it decided for a child that
binds its own compile execution. A module that plainly does compile Java read as
one that does not, and its Java roots were then withheld from the search -- where
a Kotlin or stale copy of the main class answers for it instead.
An enabled compile binding anywhere in the chain now settles it, and it is looked
for before any disabling level is allowed to decide. Extracted to a static form
taking the chain directly, because the walk needed a project on disk and was the
untested half of a method whose two halves are separately covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
attributeOf was written to replace a substring search for combine.self, and then
looked for the attribute name anywhere inside the start tag -- so xcombine.self
would have answered for it. The same rule, reproduced one level down in the fix
for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…were masking
Two failures on the JDK 8 leg, one the cause of the other.
The module's POM binds cn1:css at process-classes and the CSS compiler opens a
JFrame, which in this container is java.awt.HeadlessException before a single
test runs. Run under xvfb-run, which this job already uses for the Ant build a
few steps later.
That failure then skipped every step after it, including "Run SpotBugs for
ByteCodeTranslator" -- so the quality report failed for a missing
ByteCodeTranslator report, which said nothing about what had actually broken.
The step now runs last among this leg's gates, which also keeps its reinstall of
the maven plugin away from the SpotBugs reports the earlier steps produce.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three groups, none of which any reviewer asked for and all of which I added off
my own red-team pass:
The Settings POM-parser rewrites -- depth-matched </plugin>, combine.self and
combine.children read off their own element, comment stripping in parentPomPath,
the compilesJava chain walk, and the <sourceDirectory> gate. These change how the
Settings tool parses arbitrary user POMs, on the strength of cases nobody
reported. The parser is back to what it was.
The simulator verifier's rejected-capture diagnostics, which have nothing to do
with build hints -- I added them while chasing a screenshot failure that predates
this branch.
The release workflow's shared RELEASE_ARTIFACTS list. Only the defect codex
reported is kept: the two build hint POMs are queried with one curl each, because
two URLs share the single -o and the captured value could never be 200.
The plugin-side cleanups that rode along in the same commit as the parser work --
the unused ProcessorContext constructors and LibraryHintMerger's duplicate
prefix -- are kept, as is the comment de-duplication.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A generated file in git conflicts on every merge and there is nothing for a hand
edit to survive in, so it should never have been checked in.
It cannot simply move to one module's target/classes: BuildHints.entries() loads
it, so every consumer of the catalog needs it on its own classpath, and the
generator lives in build-hint-tools which depends on the catalog -- the catalog
cannot render its own resource without a dependency cycle. So each of the three
modules that actually needs it renders it into its own target/classes at
process-classes: maven/javase, maven/codenameone-maven-plugin and
scripts/settings/common. exec:java with classpathScope=compile, because a
plugin-level dependency resolves from the repository rather than the reactor and
would be a chicken and egg on a clean checkout. build-hint-tools is provided
scope in the two application modules so ASM cannot reach a built app.
Verified: all three render byte-identically to the file that was committed, the
catalog jar no longer carries it, and the javase jar does.
Consequences elsewhere. gen-build-hint-annotations.sh renders to a scratch
directory, and --check no longer diffs against a committed copy -- there is none
-- but asserts the rendering still succeeds and produces the full set, which is
the failure that would otherwise reach a user as an editor with no hints in it.
check-build-hint-catalog.py read the committed path and returned an empty set
when it was absent, which failed the gate OPEN; it now reads whichever module has
rendered it and says to build one when none has. The Ant JavaSE build takes it
from maven/javase/target/classes when that build has run, and without it the
simulator keeps the hints BuildHintSchemaDefaults compiles in and says the
annotated ones are missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removing the committed data file broke two catalog tests, and I did not see it
because I verified that module with -DskipTests -- on the one module whose
structure I had just changed.
BuildHints.entries() is complete only on a classpath carrying the rendered data
file. The catalog module cannot render one for its own tests: the generator lives
in build-hint-tools, which depends on the catalog. So the two assertions that
need the complete set -- every alias resolves to a real hint, and the catalog
agrees with LibraryHintMerger on every separator it defines -- move to
build-hint-tools, which renders the data into its own test classes first. Both
fail without that render, which is checked.
What stays beside the catalog is what its own sources declare, and the class now
says so rather than looking like it covers everything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:9b07db8e93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/developer-guide-docs.yml
shai-almogand others added 3 commits August 27, 2026 14:05
…lows
Making the table generated instead of committed took away the trigger it used to
get for free: while it was checked in, editing it showed up as a docs diff. The
replacement path list named the catalog and the render script but not the two
inputs that matter most -- the annotations, whose javadoc is the Description
column verbatim, and build-hint-tools, which is the renderer itself. A PR
renaming an attribute or rewording its documentation could therefore change the
guide without ever running the AsciiDoc and Vale checks over the result.
Reported by codex.
Fixed in both copies of that list in developer-guide-docs.yml, since triggering
the workflow is not enough on its own -- the HTML and PDF build is gated on the
paths-filter as well, and the two had already drifted.
And in website-docs.yml, which is the other workflow that renders the same table
and had the same gap for the same reason. Its pull_request and push lists both
already covered CodenameOne/src/**, so only the catalog, the renderer and the
script were missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two files overlapped and both auto-merged: website-docs.yml keeps master's
website telemetry step alongside the build hint table paths added here, and
CLAUDE.md keeps master's restructure -- 163 lines cut and the GC notes moved to
vm/CLAUDE.md -- alongside this branch's build hints section.
That section is rewritten rather than merged as-is: it still described writing
cn1-build-hints.json into the tree and told the reader to run the generator to
"rewrite the data file". Nothing is written into the tree any more, so it now
says where the file is rendered instead, and the command list loses the step that
no longer does anything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
windows-tooling builds the Settings tool with -Dcodename1.platform=javase, which
activates a profile declaring exec-maven-plugin with its own plugin level
<arguments> containing a <classpath/> element. Maven merges plugin level
configuration into every execution of that plugin, so that Object landed in the
generator execution's String array:
Cannot store value into array: ... can not cast one of the elements of
java.lang.Object[] to the type of the destination array, java.lang.String
A plain local build never activates that profile, which is why four green local
runs said nothing. Reproduced with the property set, fixed with
combine.self="override" on the arguments, and re-verified against the exact
command the workflow runs. Applied to all four generator executions rather than
only the one that failed: the collision needs a second declaration of the same
plugin anywhere in the effective build, which is not a property of this module.
Also restores the simulator verifier's rejected capture. It was reverted as out
of scope, and then the component inspector scenario failed a second time with a
byte identical textPixels=738 -- twice the same number is a state, not a race --
with no image kept to say what was on screen. Two failures in 22 runs on this
branch and none in 38 on any other is too specific to guess at.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:2d2708a0c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almogand others added 5 commits August 27, 2026 14:50
BytecodeComplianceMojo rewrites class files in place -- capping a class to the
supported version, and redirecting a call the runtime does not have. The build
hint manifest records the main class's own bytes, because the simulator has no
bytecode reader and can only compare the class file itself. If the main class is
one of the rewritten ones, a manifest written before that goal describes a class
that no longer exists on disk, and the simulator reads a manifest generated
moments earlier as stale and publishes none of the annotated hints. Reported by
codex.
Every pom in this repository happens to run process-annotations after this goal,
where the stamp is taken from the rewritten bytes anyway, so nothing is broken
today -- I checked all twelve. Nothing enforces that order though, and the
failure mode is silent: hints disappear, no error. Re-stamping here as well makes
the order stop mattering, since whichever of the two runs last leaves a manifest
describing the class that is actually there. It is a no-op when there is no
manifest, which is every project that declares its hints in the properties file.
The test asserts the hazard and the repair together: capping a version changes
the class, which invalidates a stamp taken before it, and re-stamping restores
the match without touching the hints. Removing the re-stamp fails it. What the
test does not cover is the one-line call from executeImpl, which needs a
MavenProject to reach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…wants
Both predate this branch without one. check-copyright-headers is scoped to the
PR's diff, so editing them is what brought them into scope -- the gate working as
designed, not a new defect. They take the Codename One GPLv2 + Classpath
Exception header their siblings in this package carry.
I ran that gate locally before the last push and it reported success while
checking nothing: with no --base it has no diff to scope to, and "0 file(s)
passed" was the tell I read straight past. Run with --base origin/master it
checks the same 128 files CI does, and passes. check-cast-semantics.sh takes
--baseline, a ratchet file rather than a git ref, so it is whole-repo and running
it bare is valid; copyright was the only gate here with that shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guide said the compiler-checked form existed and the generated table named
the annotation for each hint, but nothing anywhere showed what one looks like. A
reader had the Annotation column and no way to turn `@Ios(pods)` into code.
Adds a worked example and the exact properties lines it replaces, then explains
the three things about the syntax that are not guessable from the table: a list
hint takes a Java array and the build joins it with that hint's own separator, a
boolean hint takes Toggle rather than boolean so that leaving it out means the
build decides, and a hint with a closed value set takes an enum. Also says
plainly that the long tail and the open-ended families stay in the properties
file and that the two forms mix freely.
Every mapping in the example is checked against the generated data rather than
written from memory: each attribute resolves to the hint name claimed, each enum
constant to the wire value claimed, and ios.pods really does join with a comma.
The Java snippet compiles against the annotations, the guide's snippet validator
passes (inline blocks are refused there, so both snippets live in docs/demos),
Vale is clean, and the chapter renders without warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LanguageTool fails the developer guide build on a single match, and I had
hyphenated it in one sentence and not the other. Caught by running the gate
locally on the rendered chapter rather than by a CI cycle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A new .java file, so the diff-scoped gate checks it. I ran that gate with a base
earlier and it passed, then added this file and pushed without re-running it --
which is exactly what my own note about running diff-scoped gates AFTER the
commit is for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6fd1406078

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadPorts/JavaSE/src/com/codename1/impl/javase/Simulator.java
A project that adds @build or @DesktopBuild to its main class but never binds
cn1:process-annotations -- an upgraded or hand-written POM -- compiles and
launches fine, emits no manifest, and the simulator returned quietly and applied
none of the annotated hints. CN1BuildMojo refuses a device build for exactly this
case, so the simulator was the one place where the hints vanished with nothing
said and local behaviour diverged from device behaviour for the same project.
Reported by codex.
It now says so. A warning rather than a refusal: the simulator's job is to start,
and what it is missing are build settings, not something it cannot run without.
Detected by scanning the main class file's bytes for the annotations package,
because the simulator has no bytecode reader and an annotation's type is in the
constant pool as a descriptor. That is wider than reading the annotation table --
a main class that merely mentions the package matches too -- which is why this
warns and does not refuse.
Three cases are asserted: annotated with no manifest warns and names both the
class and the goal, an unannotated main class is silent, and a main class absent
from the classpath is silent. The first fails if the marker check is removed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:b0f5213ef4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import com.codename1.annotations.buildhints.Toggle;

// tag::buildHintAnnotations[]
@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the documentation snippet out of application bytecode

When docs/demos is built for a device through its reactor, this source is compiled into the common module even though codenameone_settings.properties names com.codenameone.developerguide.DemoCode as the main class. CN1BuildMojo.failOnMisplacedAnnotations() rejects live build-hint annotations on every non-main class, so BuildHintAnnotationSnippet makes that build fail before submission; binding process-annotations would instead fail during process-classes. Store the include outside the compiled source root or render it from a noncompiled snippet.

Useful? React with 👍 / 👎.

The developer guide's quality gate counts Vale alerts at suggestion level and
fails on any of them. I checked the chapter locally with --minAlertLevel=error,
which hides warnings, so two Microsoft.Adverbs warnings -- "deliberately" and
"freely" -- went through and reddened the build. Both sentences say the same
thing without the adverb.
Re-checked at the level CI uses: Vale 0/0/0, LanguageTool 0 matches, snippet
validator 706 blocks, paragraph capitalization clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog
shai-almog merged commit d21a389 into masterAug 27, 2026
72 checks passed
@shai-almog
shai-almog deleted the build-hint-annotations branch August 27, 2026 17:13
shai-almog added a commit that referenced this pull request Aug 27, 2026
Two conflicts. .gitignore had a new entry on each side and keeps both.
Advanced-Topics-Under-The-Hood.asciidoc is the one that mattered: master's #5586
replaced the hand-written build hint table with an include generated from
maven/build-hint-catalog, while this branch had added 28 macos.* rows to that
table. Resolving to master's include alone would have compiled cleanly and
silently deleted the documentation for every macOS build hint the port added --
the table is generated now, so a hint absent from the catalog has no
documentation anywhere.
So the 28 hints move into BuildHintsApple with the descriptions they had in the
table, and the generated table carries all 28 again. Confirmed by running
scripts/gen-build-hint-table.sh and counting them in the output, not by reading
the diff.
macNative.iosMinDeploymentTarget was already in the catalog and is NOT
duplicated; the first pass nearly added it back because a name pattern stopped
at the underscore in macos.add_libs and mis-parsed that row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Aug 27, 2026
master's #5586 made annotations the source for build hints that have one, and
the merge landed this port's 28 macos.* hints as hand-written catalog entries --
in HintGroup.MAC_NATIVE, whose key prefix is macNative. and which carries no
annotation at all. They belonged in the feature, not beside it.
HintGroup gains MAC_OS("Mac", "macos."), and @Mac declares all 28 as
compile-checked attributes: Toggle for the nine booleans, an appendable String[]
for addLibs following the ios.add_libs shape, and an explicit name for the
fifteen whose tail has dots and cannot be derived from a method name. The
hand-written entries are gone, because two sources for one hint is the drift
this feature exists to remove. macNative.* stays hand written: it is the
spelling the legacy Catalyst target reads and no annotation offers it.
Verified by regenerating rather than by reading the diff -- 28 macos rows in the
guide table, macos.entitlements.appSandbox typed boolean, and the catalog and
tools tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@shai-almog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Check build hints at compile time instead of shipping them inert - #5586

Merged
shai-almog merged 244 commits into
masterfrom
build-hint-annotations
Aug 27, 2026
Merged

Check build hints at compile time instead of shipping them inert#5586
shai-almog merged 244 commits into
masterfrom
build-hint-annotations

Conversation

@shai-almog

@shai-almogshai-almog commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

The problem

A build hint is a codename1.arg.<name>=<value> line that reaches a builder as request.getArg(name, default). Nothing checked the name, so a misspelling was accepted, copied into the build request, never read, and silently discarded — a green build with the setting simply not applied.

Our own agent reference had been shipping keys in exactly that state:

Documented in skill/references/build-hints.mdActually read by the builders
android.xPermissionsandroid.xpermissions (AndroidGradleBuilder.java:1206)
android.minSdkVersionandroid.min_sdk_version
android.sdkVersion(nothing — android.targetSDKVersion is the real one)

The change

87 hints can now be written as annotations on the application's main class, where javac does the checking: a misspelled name is an unknown symbol, a wrong value type is a type error, and a value outside a hint's supported set is an unknown enum constant.

@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)
@Build(nativeTheme = ThemeMode.MODERN)
@DesktopBuild(titleBar = DesktopTitleBar.NATIVE, width = 1280, height = 800)
@Ios(themeMode = ThemeMode.MODERN, newStorageLocation = Toggle.ON)
publicclassMyApplicationextendsLifecycle {
}

Seven annotations — @Ios, @Android, @DesktopBuild, @Build, @Hardening, @IosPrivacy, @OnDeviceDebug — and ten enums.

No attribute has a default that means anything

Every default is a marker for "nothing was said": Toggle.DEFAULT, "", {}, 0, or an enum's @HintUnset constant. An attribute left out is absent from the class file, so the processor emits nothing for it and the build decides, exactly as it does for a hint nobody wrote.

This is the reason there are no boolean attributes left. boolean appBundle() default false reads as "off unless you turn it on" while AndroidGradleBuilder defaults android.appBundle to true — and a copy of the server's answer compiled into every app already built cannot follow the server when it changes. Toggle is the three-state replacement, and a test refuses any hint attribute that declares a value-bearing default.

The builders are untouched.BuildHintAnnotationProcessor converts the annotations back into the same key/value pairs, and CN1BuildMojo merges them before the command-line overlay, the CN1Lib merges and both preflights — so a library still appends onto an annotation-supplied value and -D still wins. Simulator publishes them as system properties at startup so cn1:run sees hints that no longer live in the properties file.

The properties file is untouched too. It stays the way to set the long tail and the open-ended families such as android.permission.<NAME> that an annotation cannot express, with no new warnings or errors on that path. Declaring one hint both ways is a build error.

Where a hint is declared

Exactly one of two places:

  • CodenameOne/src/com/codename1/annotations/buildhints if it has an annotation. These are hand-written and are the source of truth for the hints they expose; BuildHintAnnotationReader reads them back with ASM rather than any file restating them.
  • maven/build-hint-catalog otherwise — dynamic families, build-service-only hints, the long tail.

The hint set used to be described in five places that had drifted apart: a prose table in the developer guide, a runtime scraper of that table in the Settings tool that guessed each type by string-matching the description, a fifteen-entry schema in the simulator, a fourteen-entry separator map in the plugin, and a hand-written agent reference. Only 147 of ~520 names appeared in more than one.

Nothing is generated into the tree

No generated file is committed. BuildHintCodeGenerator renders two views, both during a build:

  • cn1-build-hints.json for the two editors that are Codename One apps and so have no bytecode reader — the Settings tool and the simulator's hint editor. Each module that needs it renders it into its own target/classes (maven/javase, maven/codenameone-maven-plugin, scripts/settings/common). The catalog cannot render its own, because the generator lives in build-hint-tools, which depends on the catalog.
  • the developer guide's table, rendered when the guide is built.

Anything that can read bytecode reads the annotations directly and never touches the data file.

The guide's table goes from 208 rows to 570 with no prose lost, and gains Type, Default and Annotation columns it never had. An annotated hint's Default reads "set by the build" rather than a value, which is the honest statement of the paragraph above.

Enums are emitted only where the accepted set is demonstrable from the code that reads the hint — HardeningPreflight rejects an unknown harden.level, IOSDependencyManager throws on an unknown ios.dependencyManager, and GenerateDesktopAppWrapperMojo silently falls back to native on an unknown desktop.titleBar, which is precisely the failure this removes.

Scope: generated projects are deliberately not migrated here

Every project the archetype and the initializr produce is pinned to a released Codename One version — the initializr hardcodes 7.0.267 in GeneratorModel.CN1_PLUGIN_VERSION — and no released core carries com.codename1.annotations.buildhints. A generated project would import annotations that do not resolve and fail to compile before the user has written a line.

So the archetype's __mainName__.java and codenameone_settings.properties, and the initializr's common.zip and four source archives, are unchanged. They move to annotations in a follow-up once a release containing the package is out. scripts/skindesigner (7.0.255) stays on properties for the same reason, and cn1:migrate-build-hints refuses any project whose core lacks the package.

The in-repo tool projects that build against the snapshot from source are migrated: gamebuilder, video-builder, cn1playground, certificatewizard, guibuilder, fidelity-app, purchase-test-app, settings, hellocodenameone. protocol-e2e is not in that list: its only hint is codename1.arg.java.version, which has to stay in the properties file because the compiler needs it in order to compile the class that would otherwise declare it.

docs/demos is deliberately excluded: it is the developer guide's snippet project, full of intentionally incomplete fragments, and running the annotation processors over it fails by design.

Settings tool

It no longer scrapes the guide's AsciiDoc and guesses types; it reads the catalog. It also validates closed value domains, and refuses to edit a hint an annotation already owns — reading META-INF/codenameone/build-hints.properties and showing "Set by @Ios(themeMode) on the main class" — because writing a property for such a hint would create the duplicate declaration that fails the next build.

Its own tests had never run anywhere: both workflows that touched the module passed -Dmaven.test.skip=true, so every test written for the POM reader and the hint editor was dead weight. They now run in PR CI, 161 of them.

Gates

  • scripts/check-build-hint-catalog.sh fails when code reads a hint the catalog does not describe, and when our own docs or project templates name one that no builder reads. Its baseline is empty, so it is a hard gate rather than a ratchet. 497 hints read by the builders, all described.
  • scripts/gen-build-hint-annotations.sh --check asserts the render still succeeds and produces the full set. It is not a drift check — there is no committed copy to drift from — but a render that silently produced nothing would otherwise reach a user as an editor with no hints in it.
  • A JUnit suite checks the catalog's own consistency (attribute-name legality including the JLS 9.6.1 Object/Annotation method-name rule, enum domains, separators matching what LibraryHintMerger used to define), and build-hint-tools asserts the invariants that need the complete hint set, since that only exists on a classpath carrying the rendered data.
  • BytecodeComplianceMojo re-stamps the manifest after its in-place class rewrites, so the order of process-annotations against it stops mattering.
  • The developer-guide and website workflows list every input the table is rendered from — the annotations, the catalog, the renderer and the script — so a reworded attribute cannot change published documentation without running the AsciiDoc and Vale checks.

A build hint is a `codename1.arg.<name>=<value>` line that reaches a builder as
`request.getArg(name, default)`. Nothing checked the name, so a misspelling was
accepted, copied into the build request, never read, and silently discarded: a
green build with the setting simply not applied.
Our own agent reference had been shipping `android.xPermissions`,
`android.minSdkVersion` and `android.sdkVersion` for exactly that reason. The
builders read `android.xpermissions`, `android.min_sdk_version`, and nothing at
all.
Most hints can now be written as annotations on the application's main class,
where javac does the checking: a misspelled name is an unknown symbol, a wrong
value type is a type error, and a value outside a hint's supported set is an
unknown enum constant.
@iOS(newStorageLocation = true, themeMode = IosThemeMode.MODERN)
@android(minSdkVersion = 24, useAndroidX = true)
@desktop(titleBar = DesktopTitleBar.NATIVE)
public class MyApplication extends Lifecycle {
}
The builders are untouched: `BuildHintAnnotationProcessor` converts the
annotations back into the same key/value pairs and `CN1BuildMojo` merges them
before the command-line overlay, the CN1Lib merges and both preflights, so a
library still appends onto an annotation-supplied value and `-D` still wins.
`Simulator` publishes them as system properties at startup so `cn1:run` sees
hints that no longer live in the properties file.
The properties file is untouched too. It stays the way to set the long tail and
the open-ended families such as `android.permission.<NAME>` that an annotation
cannot express, with no new warnings or errors. Declaring one hint both ways is
a build error.
One catalog, five generated views
---------------------------------
The hint set had been described in five places that had drifted apart: a prose
table in the developer guide, a runtime scraper of that table in the Settings
tool that guessed each type by string-matching the description, a fifteen-entry
schema in the simulator, a fourteen-entry separator map in the plugin, and a
hand-written agent reference. Only 147 of ~520 names appeared in more than one.
`maven/build-hint-catalog` is now the single source of truth (529 hints: 457
mined from the builders, 56 documented-but-unread, 16 dynamic families; 82
exposed as annotation attributes). The annotations, the binding table the
processor reads back, the guide's table, the simulator's editor schema and the
agent reference are all generated from it. The guide's table goes from 208 rows
to 529 with no prose lost.
Enums are emitted only where the accepted set is demonstrable from the code that
reads the hint -- `HardeningPreflight` rejects an unknown `harden.level`,
`IOSDependencyManager` throws on an unknown `ios.dependencyManager`, and
`GenerateDesktopAppWrapperMojo` silently falls back to `native` on an unknown
`desktop.titleBar`, which is the failure this removes.
Generated projects
------------------
The archetype and all four initializr templates now carry the annotations, and
`cn1:migrate-build-hints` moves an existing project over. Eleven in-repo
projects are migrated. `java.version` deliberately stays in the properties file:
it picks the toolchain that compiles the class the annotations live on.
Gates
-----
`scripts/check-build-hint-catalog.sh` fails when code reads a hint the catalog
does not describe, and when our own docs or templates name one that no builder
reads. Its baseline is empty, so it is a hard gate rather than a ratchet.
`scripts/gen-build-hint-annotations.sh --check` fails on generated-file drift.
Both run in the Java 8 leg of PR CI.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:8d2cfcfde3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadmaven/pom.xml
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs[Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • build-hint-catalog: 0 findings (no issues)
    • build-hint-tools: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

`.gitignore` carries a repo-wide `**/build/*`. The catalog's package is
`com.codename1.build.shared`, so all 13 of its sources sat under a path segment
named `build` and `git add` silently skipped them. Only `pom.xml` was committed:
the module built locally from the working tree and produced an empty jar in CI,
which is why `codenameone-maven-plugin` then failed with `cannot find symbol` on
`BuildHints` and nearly every job went red.
The sibling `platform-feature-catalog` lives in the same package and is fine,
because it was added before that rule existed -- tracked files stay tracked, so
nothing ever pointed at the hazard.
Un-ignore `build` when it is a Java package rather than a build output
directory, with the rationale beside the rule so the next file added there is
not lost the same way. `maven/core/build/*` and `CodenameOne/build/*` stay
ignored.
Also from review:
- Every bare `open()` in the four Python scripts now uses a context manager, so
the handle closes even if parsing or `json.dump` raises, and the writes state
their encoding.
- The generator no longer emits an IP literal as an annotation default. PMD
reads `default "127.0.0.1"` as hardcoded configuration, and the default clause
is documentation only -- the processor emits a hint solely for members the
developer actually wrote -- so the value moves to the javadoc where it belongs.
- Files the migration touched that never carried a copyright header now have the
complete one. The archetype's `__mainName__.java` is excluded instead: it is a
template for the user's own application class, and stamping a Codename One GPL
header onto it would put our licence on their code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Pin generated projects to a version containing the annotations

The updated Initializr starter archives now import and use com.codename1.annotations.buildhints, but GeneratorModel.java:44 still generates projects pinned to CN1 7.0.267, whose core artifact predates this package. Consequently every newly generated barebones, Kotlin, Grub, or Tweet project fails compilation on the unresolved annotations unless the user manually changes the CN1 version; either defer these template changes or update the generated runtime/plugin version to the first release containing them.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The archived bootstrap ran generation at module scope, so gen_external.py's
`import gen_catalog` -- which only wants three helper functions -- rewrote every
catalog source as a side effect. Generation and its diagnostics now live in
`main()` behind a `__main__` guard, and the module-level file reads became
`load_license()` / `load_mined()` / `load_docs()`, so importing does no I/O and
cannot fail on inputs the archived copy deliberately does not carry.
Verified both directions: importing leaves the catalog untouched, and running
the two scripts end to end still reproduces the committed catalog byte for byte.
Also drops `json` and `subprocess` from check-build-hint-catalog.py. Both were
left from an earlier version that shelled out to the miner instead of importing
it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:d727c7d976

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadscripts/gen-build-hint-annotations.sh Outdated
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendscalar fallback (no native SIMD)
SIMD int-add (64K x300)java 196ms / native 73ms = 2.6x speedup
SIMD float-mul (64K x300)java 144ms / native 89ms = 1.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode83.000 ms
Base64 CN1 decode86.000 ms
Base64 native encode391.000 ms
Base64 encode ratio (CN1/native)0.212x (78.8% faster)
Base64 native decode310.000 ms
Base64 decode ratio (CN1/native)0.277x (72.3% faster)
Image encode benchmark statusskipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 60ms / native 4ms = 15.0x speedup
SIMD float-mul (64K x300)java 80ms / native 3ms = 26.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode190.000 ms
Base64 CN1 decode381.000 ms
Base64 SIMD encode99.000 ms
Base64 encode ratio (SIMD/CN1)0.521x (47.9% faster)
Base64 SIMD decode91.000 ms
Base64 decode ratio (SIMD/CN1)0.239x (76.1% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)22.000 ms
Image createMask (SIMD on)15.000 ms
Image createMask ratio (SIMD on/off)0.682x (31.8% faster)
Image applyMask (SIMD off)71.000 ms
Image applyMask (SIMD on)35.000 ms
Image applyMask ratio (SIMD on/off)0.493x (50.7% faster)
Image modifyAlpha (SIMD off)44.000 ms
Image modifyAlpha (SIMD on)22.000 ms
Image modifyAlpha ratio (SIMD on/off)0.500x (50.0% faster)
Image modifyAlpha removeColor (SIMD off)34.000 ms
Image modifyAlpha removeColor (SIMD on)25.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.735x (26.5% faster)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 63ms / native 5ms = 12.6x speedup
SIMD float-mul (64K x300)java 63ms / native 4ms = 15.7x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode327.000 ms
Base64 CN1 decode216.000 ms
Base64 SIMD encode176.000 ms
Base64 encode ratio (SIMD/CN1)0.538x (46.2% faster)
Base64 SIMD decode132.000 ms
Base64 decode ratio (SIMD/CN1)0.611x (38.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)28.000 ms
Image createMask (SIMD on)23.000 ms
Image createMask ratio (SIMD on/off)0.821x (17.9% faster)
Image applyMask (SIMD off)58.000 ms
Image applyMask (SIMD on)56.000 ms
Image applyMask ratio (SIMD on/off)0.966x (3.4% faster)
Image modifyAlpha (SIMD off)66.000 ms
Image modifyAlpha (SIMD on)28.000 ms
Image modifyAlpha ratio (SIMD on/off)0.424x (57.6% faster)
Image modifyAlpha removeColor (SIMD off)44.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.727x (27.3% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 55ms / native 3ms = 18.3x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode268.000 ms
Base64 CN1 decode152.000 ms
Base64 SIMD encode66.000 ms
Base64 encode ratio (SIMD/CN1)0.246x (75.4% faster)
Base64 SIMD decode64.000 ms
Base64 decode ratio (SIMD/CN1)0.421x (57.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)13.000 ms
Image createMask (SIMD on)9.000 ms
Image createMask ratio (SIMD on/off)0.692x (30.8% faster)
Image applyMask (SIMD off)24.000 ms
Image applyMask (SIMD on)20.000 ms
Image applyMask ratio (SIMD on/off)0.833x (16.7% faster)
Image modifyAlpha (SIMD off)17.000 ms
Image modifyAlpha (SIMD on)12.000 ms
Image modifyAlpha ratio (SIMD on/off)0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off)21.000 ms
Image modifyAlpha removeColor (SIMD on)13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.619x (38.1% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

Three separate gates rejected generated output. Each is fixed in the generator
so the class of problem cannot come back through a catalog edit.
Unmappable characters. The prose is imported from the developer guide, which
uses typographic punctuation, and `CodenameOne/src` is also compiled by an Ant
javac step with ASCII encoding where a single em dash is
`error: unmappable character for encoding ASCII` -- a build failure, not a
warning. A Unicode escape would not have helped: javac expands `\uXXXX` before
it strips comments, so the character reappears. `toAscii` now folds the
punctuation that actually occurs, and *refuses* anything it has no mapping for
rather than dropping it, because silently deleting a character from a hint's
documentation is the worse outcome.
Broken table. `ios.spm.packages` is documented as `identity|url|requirement`,
and a bare `|` starts a new AsciiDoc cell, so asciidoctor reported "dropping
cells from incomplete row" for the whole 529-row table. Cells are escaped now.
Vale. The guide enforces the Microsoft style as errors, and the generated table
feeds it, so the catalog's prose has to satisfy it too: contractions, no
"and so on", no stray adverbs. A default value is not prose, though -- the one
remaining hit was `android.file_paths`, whose default is an XML fragment -- so
a quoted default now carries the `// vale-skip:` comment .vale.ini documents
for individual false positives.
Also fixes a data bug the guide exposed. The miner preserved Java escape
sequences instead of decoding them, so `android.file_paths` and
`android.facebook_permissions` recorded defaults containing literal
backslashes that the build never sees, and those reached the rendered table.
The miner decodes escapes and re-quotes safely, and the two catalog entries are
corrected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/build_hint_miner.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:96bff9038a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

`killedThreadReportsItselfFinished` failed the Java 21 leg with
"FormTest timed out after 5000ms; edt=initialized pendingSerialCalls=0". The
waits in this class used a 5000ms deadline, which is exactly the `@FormTest`
timeout in EDTTestInterceptor -- so on a loaded runner the poll loop consumed
the entire harness budget and the interceptor fired first. The report then said
only that the method timed out, with nothing about which condition never became
true.
The waits now use 2000ms, well inside the harness budget and still roughly two
thousand times the ~1ms these threads actually take to stop. A genuine
regression now fails on the test's own assertion, which names what went wrong.
Pre-existing (the test arrived with #5526) and unrelated to the build hint work:
core-unittests has no dependency on the JavaSE port, so none of the simulator
registration in this branch runs there, this branch changes nothing under
com.codename1.db or EasyThread, and the Java 8 leg passed the same commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:a343fe3335

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

A mojo's defaultPhase does not bind it to a project -- the project's POM has to
-- and nothing turns a build hint annotation back into a codename1.arg.* pair
except the process-annotations goal. So migrating a project without that
binding deleted working properties and replaced them with annotations no goal
ever reads: the hints vanished from the build with no diagnostic anywhere.
Five projects in this branch were already in that state. gamebuilder,
docs/demos, video-builder and cn1playground bind the plugin but not that goal,
so the binding is added. input-validation-app's common module has no build
section at all, so its migration is reverted rather than inventing a lifecycle
for a demo app.
The goal now checks the reactor for the binding and refuses with the execution
block to paste, so this cannot happen to anyone else.
Three more from the same review:
- The deletion pass recognized only `key=value`. `Properties.load` also accepts
`key:value`, `key value`, escaped separators inside the key, and logical
continuation lines; a declaration it failed to match was left behind while the
annotation was added, so the next build failed with the duplicate-hint error
this goal exists to prevent. Keys are parsed the way Properties.load defines
them now, with a unit test per form.
- The settings file was read as ISO-8859-1 and written back as UTF-8, turning
any unrelated non-ASCII byte -- an accented displayName, say -- into mojibake.
It is written back as ISO-8859-1.
- cn1.androidTheme and cn1.nativeTheme are deprecated aliases of and.themeMode
and nativeTheme, which the builders honour as fallbacks. Neither declared
aliasOf, so conflict detection missed them and one value silently won.
Also: the generation script rebuilt the generator only when its class was
absent, so editing a catalog source and rerunning regenerated every view from
the previous build's bytecode -- reporting success while ignoring the edit, and
passing --check on a tree that was genuinely stale. It always rebuilds now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:0edef42ca4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…plicating a hint
docs/demos is the developer guide's snippet project: deliberately incomplete
code fragments that illustrate @entity, @route, @AppIntent and @mapped. Binding
process-annotations there put those snippets in front of the other processors,
which correctly rejected six of them, so the migration is reverted and its two
hints are back in the properties file. That the project omitted the goal was the
point, not an oversight.
The other three newly bound projects were checked rather than assumed:
gamebuilder, video-builder and cn1playground each run process-annotations
cleanly and emit 6, 3 and 5 hints respectively.
Settings could still create the duplicate the migration is careful to avoid. In
a generated project ios.themeMode and its neighbours are annotations, but the
Build Hints UI decides a hint is inactive from the properties file alone and its
Add button writes a property -- producing a second declaration that fails the
next build. The tool now reads META-INF/codenameone/build-hints.properties, the
file the processor writes on every build and deletes when the last annotation
goes, and renders those hints read-only with the attribute that owns them:
"Set by @iOS(themeMode) on the main class." An unbuilt project has no such file
and behaves as before.
Also fixes the SpotBugs finding this branch introduced: `backslashes % 2 == 1`
in the continuation scan is false for negative odd numbers, so it is `!= 0`.
The count cannot go negative, but the idiom is wrong regardless of that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Keep Initializr templates compatible with the pinned runtime

Every Initializr source archive now imports com.codename1.annotations.buildhints and uses the new annotations, while GeneratorModel.CN1_PLUGIN_VERSION still rewrites generated projects to 7.0.267, whose codenameone-core predates that package; the generated common POM also omits process-annotations. Consequently all newly downloaded Initializr projects fail compilation instead of receiving the defaults removed from common.zip's settings file. Leave these templates property-backed until Initializr targets the release containing this feature, or bump the generated version and bind the processor.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 339 seconds

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 62ms / native 3ms = 20.6x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathactive (NEON-accelerated)
Base64 CN1 encode167.000 ms
Base64 CN1 decode120.000 ms
Base64 native encode624.000 ms
Base64 encode ratio (CN1/native)0.268x (73.2% faster)
Base64 native decode231.000 ms
Base64 decode ratio (CN1/native)0.519x (48.1% faster)
Base64 SIMD encode56.000 ms
Base64 encode ratio (SIMD/CN1)0.335x (66.5% faster)
Base64 SIMD decode45.000 ms
Base64 decode ratio (SIMD/CN1)0.375x (62.5% faster)
Base64 encode ratio (SIMD/native)0.090x (91.0% faster)
Base64 decode ratio (SIMD/native)0.195x (80.5% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)6.000 ms
Image createMask (SIMD on)2.000 ms
Image createMask ratio (SIMD on/off)0.333x (66.7% faster)
Image applyMask (SIMD off)46.000 ms
Image applyMask (SIMD on)36.000 ms
Image applyMask ratio (SIMD on/off)0.783x (21.7% faster)
Image modifyAlpha (SIMD off)39.000 ms
Image modifyAlpha (SIMD on)39.000 ms
Image modifyAlpha ratio (SIMD on/off)1.000x (0.0% slower)
Image modifyAlpha removeColor (SIMD off)47.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.681x (31.9% faster)

Every project the archetype and the initializr produce is pinned to a released
Codename One version -- the initializr hardcodes 7.0.267 in
GeneratorModel.CN1_PLUGIN_VERSION -- and no released core carries
com.codename1.annotations.buildhints. So a generated project would import
annotations that do not resolve and fail to compile before the user has written
a line, and the settings those templates stopped declaring would simply be gone.
The templates are reverted to exactly their previous state: the archetype's
__mainName__.java and codenameone_settings.properties, and the initializr's
common.zip and four source archives. They can move to annotations in a follow-up
once a release containing the package is out.
The generated build hint table is dropped from the agent skill reference for the
same reason -- it documented a form those projects cannot use yet -- so the
generator no longer rewrites markdown at all.
What stays from that area is unrelated to annotations: the skill reference
described build hints that no builder reads, so a reader copying them got a
green build and no effect. android.xPermissions is spelled android.xpermissions,
android.minSdkVersion is android.min_sdk_version, and android.sdkVersion,
android.googlePlayVersion, build.compile, build.timeout, javascript.html5,
javascript.bundleResources and ios.orientation do not exist at all. Those
corrections are right for the published version too, and the catalog gate now
holds our own documentation to them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
compilesJava() already withheld the conventional src/main/java from the search
when the POM chain switches default-compile off with <phase>none</phase> and
binds nothing in its place. <sourceDirectory> is the same root, declared instead
of assumed, and it was not gated -- so a module that compiles nothing with javac
still offered it, and a stale copy of the main class there answered ahead of the
compiled source.
Per element rather than over the whole list, because a Kotlin-only module is
exactly the case where javac does not run and its <sourceDirs> must still be
searched. Both directions are asserted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:705783d9b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/release-on-maven-central.yml
shai-almogand others added 10 commits August 27, 2026 11:08
…writes
The .java branch in write() guarded against a non-ASCII character reaching a
generated Java source; the generator writes a JSON data file and an asciidoc
table and nothing else. toAscii survives because the reader test still needs it
-- the catalog's prose came from the developer guide and the annotations are
ASCII Java sources, so the two cannot be compared verbatim -- and its
documentation now says that rather than describing a build step it no longer
feeds.
Also the HintGroup local in Bindings that nothing read, and the no-op continue
at the end of the loop that was its only use. toHint already resolves the group
and throws when an annotation type names none.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Central recovery poll queried the two build hint artifacts with a single
curl carrying two URLs. curl needs an -o per URL and emits --write-out after
each transfer, so the second POM body arrived on stdout with both status codes
appended: the captured value could never equal 200, and the poll -- which exists
to rescue a release Central has already accepted -- would exhaust all 90
attempts and fail it. Reported by codex.
Both this poll and the R2 confirmation walk a list of artifacts, and each
carried its own copy, which had already drifted: Central's omitted
codenameone-core. The list is now named once in the job environment and both
read it, with a guard that refuses an empty list rather than reporting success
for having checked nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aliasOf, deprecated, external, enterpriseOnly and link exist so an annotated hint
can say what the catalog entries already say, and none of the 87 shipped
attributes needs any of them -- so the reader's handling of all five was carried
by no test at all. Exercised now against a copy of the real annotation package
with one probe attribute added, which is what the reader compiles anyway.
Kept rather than deleted: the first annotated hint that has to be marked
deprecated needs somewhere to say so, and would otherwise be the first thing ever
to run that path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
validateScreenshotContent threw before the write, so a failing scenario left
nothing behind but a pixel count -- which is exactly what happened to the
component-inspector run that reported textPixels=738 with no image to look at.
The capture is now written as <name>.png.rejected.png first, into the directory
the workflow already uploads with if: always(), so the next occurrence can be
diagnosed instead of guessed at.
Not a fix for that failure: the check reads a fixed screen rectangle and the run
before and after it were green on the same commit, so what it saw is still
unknown. This is what makes finding out possible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e</phase>
compilesJava() answered from the first level in the chain that switched
default-compile off, so an ancestor that disables it decided for a child that
binds its own compile execution. A module that plainly does compile Java read as
one that does not, and its Java roots were then withheld from the search -- where
a Kotlin or stale copy of the main class answers for it instead.
An enabled compile binding anywhere in the chain now settles it, and it is looked
for before any disabling level is allowed to decide. Extracted to a static form
taking the chain directly, because the walk needed a project on disk and was the
untested half of a method whose two halves are separately covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
attributeOf was written to replace a substring search for combine.self, and then
looked for the attribute name anywhere inside the start tag -- so xcombine.self
would have answered for it. The same rule, reproduced one level down in the fix
for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…were masking
Two failures on the JDK 8 leg, one the cause of the other.
The module's POM binds cn1:css at process-classes and the CSS compiler opens a
JFrame, which in this container is java.awt.HeadlessException before a single
test runs. Run under xvfb-run, which this job already uses for the Ant build a
few steps later.
That failure then skipped every step after it, including "Run SpotBugs for
ByteCodeTranslator" -- so the quality report failed for a missing
ByteCodeTranslator report, which said nothing about what had actually broken.
The step now runs last among this leg's gates, which also keeps its reinstall of
the maven plugin away from the SpotBugs reports the earlier steps produce.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three groups, none of which any reviewer asked for and all of which I added off
my own red-team pass:
The Settings POM-parser rewrites -- depth-matched </plugin>, combine.self and
combine.children read off their own element, comment stripping in parentPomPath,
the compilesJava chain walk, and the <sourceDirectory> gate. These change how the
Settings tool parses arbitrary user POMs, on the strength of cases nobody
reported. The parser is back to what it was.
The simulator verifier's rejected-capture diagnostics, which have nothing to do
with build hints -- I added them while chasing a screenshot failure that predates
this branch.
The release workflow's shared RELEASE_ARTIFACTS list. Only the defect codex
reported is kept: the two build hint POMs are queried with one curl each, because
two URLs share the single -o and the captured value could never be 200.
The plugin-side cleanups that rode along in the same commit as the parser work --
the unused ProcessorContext constructors and LibraryHintMerger's duplicate
prefix -- are kept, as is the comment de-duplication.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A generated file in git conflicts on every merge and there is nothing for a hand
edit to survive in, so it should never have been checked in.
It cannot simply move to one module's target/classes: BuildHints.entries() loads
it, so every consumer of the catalog needs it on its own classpath, and the
generator lives in build-hint-tools which depends on the catalog -- the catalog
cannot render its own resource without a dependency cycle. So each of the three
modules that actually needs it renders it into its own target/classes at
process-classes: maven/javase, maven/codenameone-maven-plugin and
scripts/settings/common. exec:java with classpathScope=compile, because a
plugin-level dependency resolves from the repository rather than the reactor and
would be a chicken and egg on a clean checkout. build-hint-tools is provided
scope in the two application modules so ASM cannot reach a built app.
Verified: all three render byte-identically to the file that was committed, the
catalog jar no longer carries it, and the javase jar does.
Consequences elsewhere. gen-build-hint-annotations.sh renders to a scratch
directory, and --check no longer diffs against a committed copy -- there is none
-- but asserts the rendering still succeeds and produces the full set, which is
the failure that would otherwise reach a user as an editor with no hints in it.
check-build-hint-catalog.py read the committed path and returned an empty set
when it was absent, which failed the gate OPEN; it now reads whichever module has
rendered it and says to build one when none has. The Ant JavaSE build takes it
from maven/javase/target/classes when that build has run, and without it the
simulator keeps the hints BuildHintSchemaDefaults compiles in and says the
annotated ones are missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removing the committed data file broke two catalog tests, and I did not see it
because I verified that module with -DskipTests -- on the one module whose
structure I had just changed.
BuildHints.entries() is complete only on a classpath carrying the rendered data
file. The catalog module cannot render one for its own tests: the generator lives
in build-hint-tools, which depends on the catalog. So the two assertions that
need the complete set -- every alias resolves to a real hint, and the catalog
agrees with LibraryHintMerger on every separator it defines -- move to
build-hint-tools, which renders the data into its own test classes first. Both
fail without that render, which is checked.
What stays beside the catalog is what its own sources declare, and the class now
says so rather than looking like it covers everything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:9b07db8e93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/developer-guide-docs.yml
shai-almogand others added 3 commits August 27, 2026 14:05
…lows
Making the table generated instead of committed took away the trigger it used to
get for free: while it was checked in, editing it showed up as a docs diff. The
replacement path list named the catalog and the render script but not the two
inputs that matter most -- the annotations, whose javadoc is the Description
column verbatim, and build-hint-tools, which is the renderer itself. A PR
renaming an attribute or rewording its documentation could therefore change the
guide without ever running the AsciiDoc and Vale checks over the result.
Reported by codex.
Fixed in both copies of that list in developer-guide-docs.yml, since triggering
the workflow is not enough on its own -- the HTML and PDF build is gated on the
paths-filter as well, and the two had already drifted.
And in website-docs.yml, which is the other workflow that renders the same table
and had the same gap for the same reason. Its pull_request and push lists both
already covered CodenameOne/src/**, so only the catalog, the renderer and the
script were missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two files overlapped and both auto-merged: website-docs.yml keeps master's
website telemetry step alongside the build hint table paths added here, and
CLAUDE.md keeps master's restructure -- 163 lines cut and the GC notes moved to
vm/CLAUDE.md -- alongside this branch's build hints section.
That section is rewritten rather than merged as-is: it still described writing
cn1-build-hints.json into the tree and told the reader to run the generator to
"rewrite the data file". Nothing is written into the tree any more, so it now
says where the file is rendered instead, and the command list loses the step that
no longer does anything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
windows-tooling builds the Settings tool with -Dcodename1.platform=javase, which
activates a profile declaring exec-maven-plugin with its own plugin level
<arguments> containing a <classpath/> element. Maven merges plugin level
configuration into every execution of that plugin, so that Object landed in the
generator execution's String array:
Cannot store value into array: ... can not cast one of the elements of
java.lang.Object[] to the type of the destination array, java.lang.String
A plain local build never activates that profile, which is why four green local
runs said nothing. Reproduced with the property set, fixed with
combine.self="override" on the arguments, and re-verified against the exact
command the workflow runs. Applied to all four generator executions rather than
only the one that failed: the collision needs a second declaration of the same
plugin anywhere in the effective build, which is not a property of this module.
Also restores the simulator verifier's rejected capture. It was reverted as out
of scope, and then the component inspector scenario failed a second time with a
byte identical textPixels=738 -- twice the same number is a state, not a race --
with no image kept to say what was on screen. Two failures in 22 runs on this
branch and none in 38 on any other is too specific to guess at.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:2d2708a0c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almogand others added 5 commits August 27, 2026 14:50
BytecodeComplianceMojo rewrites class files in place -- capping a class to the
supported version, and redirecting a call the runtime does not have. The build
hint manifest records the main class's own bytes, because the simulator has no
bytecode reader and can only compare the class file itself. If the main class is
one of the rewritten ones, a manifest written before that goal describes a class
that no longer exists on disk, and the simulator reads a manifest generated
moments earlier as stale and publishes none of the annotated hints. Reported by
codex.
Every pom in this repository happens to run process-annotations after this goal,
where the stamp is taken from the rewritten bytes anyway, so nothing is broken
today -- I checked all twelve. Nothing enforces that order though, and the
failure mode is silent: hints disappear, no error. Re-stamping here as well makes
the order stop mattering, since whichever of the two runs last leaves a manifest
describing the class that is actually there. It is a no-op when there is no
manifest, which is every project that declares its hints in the properties file.
The test asserts the hazard and the repair together: capping a version changes
the class, which invalidates a stamp taken before it, and re-stamping restores
the match without touching the hints. Removing the re-stamp fails it. What the
test does not cover is the one-line call from executeImpl, which needs a
MavenProject to reach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…wants
Both predate this branch without one. check-copyright-headers is scoped to the
PR's diff, so editing them is what brought them into scope -- the gate working as
designed, not a new defect. They take the Codename One GPLv2 + Classpath
Exception header their siblings in this package carry.
I ran that gate locally before the last push and it reported success while
checking nothing: with no --base it has no diff to scope to, and "0 file(s)
passed" was the tell I read straight past. Run with --base origin/master it
checks the same 128 files CI does, and passes. check-cast-semantics.sh takes
--baseline, a ratchet file rather than a git ref, so it is whole-repo and running
it bare is valid; copyright was the only gate here with that shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guide said the compiler-checked form existed and the generated table named
the annotation for each hint, but nothing anywhere showed what one looks like. A
reader had the Annotation column and no way to turn `@Ios(pods)` into code.
Adds a worked example and the exact properties lines it replaces, then explains
the three things about the syntax that are not guessable from the table: a list
hint takes a Java array and the build joins it with that hint's own separator, a
boolean hint takes Toggle rather than boolean so that leaving it out means the
build decides, and a hint with a closed value set takes an enum. Also says
plainly that the long tail and the open-ended families stay in the properties
file and that the two forms mix freely.
Every mapping in the example is checked against the generated data rather than
written from memory: each attribute resolves to the hint name claimed, each enum
constant to the wire value claimed, and ios.pods really does join with a comma.
The Java snippet compiles against the annotations, the guide's snippet validator
passes (inline blocks are refused there, so both snippets live in docs/demos),
Vale is clean, and the chapter renders without warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LanguageTool fails the developer guide build on a single match, and I had
hyphenated it in one sentence and not the other. Caught by running the gate
locally on the rendered chapter rather than by a CI cycle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A new .java file, so the diff-scoped gate checks it. I ran that gate with a base
earlier and it passed, then added this file and pushed without re-running it --
which is exactly what my own note about running diff-scoped gates AFTER the
commit is for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6fd1406078

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadPorts/JavaSE/src/com/codename1/impl/javase/Simulator.java
A project that adds @build or @DesktopBuild to its main class but never binds
cn1:process-annotations -- an upgraded or hand-written POM -- compiles and
launches fine, emits no manifest, and the simulator returned quietly and applied
none of the annotated hints. CN1BuildMojo refuses a device build for exactly this
case, so the simulator was the one place where the hints vanished with nothing
said and local behaviour diverged from device behaviour for the same project.
Reported by codex.
It now says so. A warning rather than a refusal: the simulator's job is to start,
and what it is missing are build settings, not something it cannot run without.
Detected by scanning the main class file's bytes for the annotations package,
because the simulator has no bytecode reader and an annotation's type is in the
constant pool as a descriptor. That is wider than reading the annotation table --
a main class that merely mentions the package matches too -- which is why this
warns and does not refuse.
Three cases are asserted: annotated with no manifest warns and names both the
class and the goal, an unannotated main class is silent, and a main class absent
from the classpath is silent. The first fails if the marker check is removed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:b0f5213ef4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import com.codename1.annotations.buildhints.Toggle;

// tag::buildHintAnnotations[]
@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the documentation snippet out of application bytecode

When docs/demos is built for a device through its reactor, this source is compiled into the common module even though codenameone_settings.properties names com.codenameone.developerguide.DemoCode as the main class. CN1BuildMojo.failOnMisplacedAnnotations() rejects live build-hint annotations on every non-main class, so BuildHintAnnotationSnippet makes that build fail before submission; binding process-annotations would instead fail during process-classes. Store the include outside the compiled source root or render it from a noncompiled snippet.

Useful? React with 👍 / 👎.

The developer guide's quality gate counts Vale alerts at suggestion level and
fails on any of them. I checked the chapter locally with --minAlertLevel=error,
which hides warnings, so two Microsoft.Adverbs warnings -- "deliberately" and
"freely" -- went through and reddened the build. Both sentences say the same
thing without the adverb.
Re-checked at the level CI uses: Vale 0/0/0, LanguageTool 0 matches, snippet
validator 706 blocks, paragraph capitalization clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog
shai-almog merged commit d21a389 into masterAug 27, 2026
72 checks passed
@shai-almog
shai-almog deleted the build-hint-annotations branch August 27, 2026 17:13
shai-almog added a commit that referenced this pull request Aug 27, 2026
Two conflicts. .gitignore had a new entry on each side and keeps both.
Advanced-Topics-Under-The-Hood.asciidoc is the one that mattered: master's #5586
replaced the hand-written build hint table with an include generated from
maven/build-hint-catalog, while this branch had added 28 macos.* rows to that
table. Resolving to master's include alone would have compiled cleanly and
silently deleted the documentation for every macOS build hint the port added --
the table is generated now, so a hint absent from the catalog has no
documentation anywhere.
So the 28 hints move into BuildHintsApple with the descriptions they had in the
table, and the generated table carries all 28 again. Confirmed by running
scripts/gen-build-hint-table.sh and counting them in the output, not by reading
the diff.
macNative.iosMinDeploymentTarget was already in the catalog and is NOT
duplicated; the first pass nearly added it back because a name pattern stopped
at the underscore in macos.add_libs and mis-parsed that row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Aug 27, 2026
master's #5586 made annotations the source for build hints that have one, and
the merge landed this port's 28 macos.* hints as hand-written catalog entries --
in HintGroup.MAC_NATIVE, whose key prefix is macNative. and which carries no
annotation at all. They belonged in the feature, not beside it.
HintGroup gains MAC_OS("Mac", "macos."), and @Mac declares all 28 as
compile-checked attributes: Toggle for the nine booleans, an appendable String[]
for addLibs following the ios.add_libs shape, and an explicit name for the
fifteen whose tail has dots and cannot be derived from a method name. The
hand-written entries are gone, because two sources for one hint is the drift
this feature exists to remove. macNative.* stays hand written: it is the
spelling the legacy Catalyst target reads and no annotation offers it.
Verified by regenerating rather than by reading the diff -- 28 macos rows in the
guide table, macos.entitlements.appSandbox typed boolean, and the catalog and
tools tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@shai-almog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Check build hints at compile time instead of shipping them inert - #5586

Merged
shai-almog merged 244 commits into
masterfrom
build-hint-annotations
Aug 27, 2026
Merged

Check build hints at compile time instead of shipping them inert#5586
shai-almog merged 244 commits into
masterfrom
build-hint-annotations

Conversation

@shai-almog

@shai-almogshai-almog commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

The problem

A build hint is a codename1.arg.<name>=<value> line that reaches a builder as request.getArg(name, default). Nothing checked the name, so a misspelling was accepted, copied into the build request, never read, and silently discarded — a green build with the setting simply not applied.

Our own agent reference had been shipping keys in exactly that state:

Documented in skill/references/build-hints.mdActually read by the builders
android.xPermissionsandroid.xpermissions (AndroidGradleBuilder.java:1206)
android.minSdkVersionandroid.min_sdk_version
android.sdkVersion(nothing — android.targetSDKVersion is the real one)

The change

87 hints can now be written as annotations on the application's main class, where javac does the checking: a misspelled name is an unknown symbol, a wrong value type is a type error, and a value outside a hint's supported set is an unknown enum constant.

@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)
@Build(nativeTheme = ThemeMode.MODERN)
@DesktopBuild(titleBar = DesktopTitleBar.NATIVE, width = 1280, height = 800)
@Ios(themeMode = ThemeMode.MODERN, newStorageLocation = Toggle.ON)
publicclassMyApplicationextendsLifecycle {
}

Seven annotations — @Ios, @Android, @DesktopBuild, @Build, @Hardening, @IosPrivacy, @OnDeviceDebug — and ten enums.

No attribute has a default that means anything

Every default is a marker for "nothing was said": Toggle.DEFAULT, "", {}, 0, or an enum's @HintUnset constant. An attribute left out is absent from the class file, so the processor emits nothing for it and the build decides, exactly as it does for a hint nobody wrote.

This is the reason there are no boolean attributes left. boolean appBundle() default false reads as "off unless you turn it on" while AndroidGradleBuilder defaults android.appBundle to true — and a copy of the server's answer compiled into every app already built cannot follow the server when it changes. Toggle is the three-state replacement, and a test refuses any hint attribute that declares a value-bearing default.

The builders are untouched.BuildHintAnnotationProcessor converts the annotations back into the same key/value pairs, and CN1BuildMojo merges them before the command-line overlay, the CN1Lib merges and both preflights — so a library still appends onto an annotation-supplied value and -D still wins. Simulator publishes them as system properties at startup so cn1:run sees hints that no longer live in the properties file.

The properties file is untouched too. It stays the way to set the long tail and the open-ended families such as android.permission.<NAME> that an annotation cannot express, with no new warnings or errors on that path. Declaring one hint both ways is a build error.

Where a hint is declared

Exactly one of two places:

  • CodenameOne/src/com/codename1/annotations/buildhints if it has an annotation. These are hand-written and are the source of truth for the hints they expose; BuildHintAnnotationReader reads them back with ASM rather than any file restating them.
  • maven/build-hint-catalog otherwise — dynamic families, build-service-only hints, the long tail.

The hint set used to be described in five places that had drifted apart: a prose table in the developer guide, a runtime scraper of that table in the Settings tool that guessed each type by string-matching the description, a fifteen-entry schema in the simulator, a fourteen-entry separator map in the plugin, and a hand-written agent reference. Only 147 of ~520 names appeared in more than one.

Nothing is generated into the tree

No generated file is committed. BuildHintCodeGenerator renders two views, both during a build:

  • cn1-build-hints.json for the two editors that are Codename One apps and so have no bytecode reader — the Settings tool and the simulator's hint editor. Each module that needs it renders it into its own target/classes (maven/javase, maven/codenameone-maven-plugin, scripts/settings/common). The catalog cannot render its own, because the generator lives in build-hint-tools, which depends on the catalog.
  • the developer guide's table, rendered when the guide is built.

Anything that can read bytecode reads the annotations directly and never touches the data file.

The guide's table goes from 208 rows to 570 with no prose lost, and gains Type, Default and Annotation columns it never had. An annotated hint's Default reads "set by the build" rather than a value, which is the honest statement of the paragraph above.

Enums are emitted only where the accepted set is demonstrable from the code that reads the hint — HardeningPreflight rejects an unknown harden.level, IOSDependencyManager throws on an unknown ios.dependencyManager, and GenerateDesktopAppWrapperMojo silently falls back to native on an unknown desktop.titleBar, which is precisely the failure this removes.

Scope: generated projects are deliberately not migrated here

Every project the archetype and the initializr produce is pinned to a released Codename One version — the initializr hardcodes 7.0.267 in GeneratorModel.CN1_PLUGIN_VERSION — and no released core carries com.codename1.annotations.buildhints. A generated project would import annotations that do not resolve and fail to compile before the user has written a line.

So the archetype's __mainName__.java and codenameone_settings.properties, and the initializr's common.zip and four source archives, are unchanged. They move to annotations in a follow-up once a release containing the package is out. scripts/skindesigner (7.0.255) stays on properties for the same reason, and cn1:migrate-build-hints refuses any project whose core lacks the package.

The in-repo tool projects that build against the snapshot from source are migrated: gamebuilder, video-builder, cn1playground, certificatewizard, guibuilder, fidelity-app, purchase-test-app, settings, hellocodenameone. protocol-e2e is not in that list: its only hint is codename1.arg.java.version, which has to stay in the properties file because the compiler needs it in order to compile the class that would otherwise declare it.

docs/demos is deliberately excluded: it is the developer guide's snippet project, full of intentionally incomplete fragments, and running the annotation processors over it fails by design.

Settings tool

It no longer scrapes the guide's AsciiDoc and guesses types; it reads the catalog. It also validates closed value domains, and refuses to edit a hint an annotation already owns — reading META-INF/codenameone/build-hints.properties and showing "Set by @Ios(themeMode) on the main class" — because writing a property for such a hint would create the duplicate declaration that fails the next build.

Its own tests had never run anywhere: both workflows that touched the module passed -Dmaven.test.skip=true, so every test written for the POM reader and the hint editor was dead weight. They now run in PR CI, 161 of them.

Gates

  • scripts/check-build-hint-catalog.sh fails when code reads a hint the catalog does not describe, and when our own docs or project templates name one that no builder reads. Its baseline is empty, so it is a hard gate rather than a ratchet. 497 hints read by the builders, all described.
  • scripts/gen-build-hint-annotations.sh --check asserts the render still succeeds and produces the full set. It is not a drift check — there is no committed copy to drift from — but a render that silently produced nothing would otherwise reach a user as an editor with no hints in it.
  • A JUnit suite checks the catalog's own consistency (attribute-name legality including the JLS 9.6.1 Object/Annotation method-name rule, enum domains, separators matching what LibraryHintMerger used to define), and build-hint-tools asserts the invariants that need the complete hint set, since that only exists on a classpath carrying the rendered data.
  • BytecodeComplianceMojo re-stamps the manifest after its in-place class rewrites, so the order of process-annotations against it stops mattering.
  • The developer-guide and website workflows list every input the table is rendered from — the annotations, the catalog, the renderer and the script — so a reworded attribute cannot change published documentation without running the AsciiDoc and Vale checks.

A build hint is a `codename1.arg.<name>=<value>` line that reaches a builder as
`request.getArg(name, default)`. Nothing checked the name, so a misspelling was
accepted, copied into the build request, never read, and silently discarded: a
green build with the setting simply not applied.
Our own agent reference had been shipping `android.xPermissions`,
`android.minSdkVersion` and `android.sdkVersion` for exactly that reason. The
builders read `android.xpermissions`, `android.min_sdk_version`, and nothing at
all.
Most hints can now be written as annotations on the application's main class,
where javac does the checking: a misspelled name is an unknown symbol, a wrong
value type is a type error, and a value outside a hint's supported set is an
unknown enum constant.
@iOS(newStorageLocation = true, themeMode = IosThemeMode.MODERN)
@android(minSdkVersion = 24, useAndroidX = true)
@desktop(titleBar = DesktopTitleBar.NATIVE)
public class MyApplication extends Lifecycle {
}
The builders are untouched: `BuildHintAnnotationProcessor` converts the
annotations back into the same key/value pairs and `CN1BuildMojo` merges them
before the command-line overlay, the CN1Lib merges and both preflights, so a
library still appends onto an annotation-supplied value and `-D` still wins.
`Simulator` publishes them as system properties at startup so `cn1:run` sees
hints that no longer live in the properties file.
The properties file is untouched too. It stays the way to set the long tail and
the open-ended families such as `android.permission.<NAME>` that an annotation
cannot express, with no new warnings or errors. Declaring one hint both ways is
a build error.
One catalog, five generated views
---------------------------------
The hint set had been described in five places that had drifted apart: a prose
table in the developer guide, a runtime scraper of that table in the Settings
tool that guessed each type by string-matching the description, a fifteen-entry
schema in the simulator, a fourteen-entry separator map in the plugin, and a
hand-written agent reference. Only 147 of ~520 names appeared in more than one.
`maven/build-hint-catalog` is now the single source of truth (529 hints: 457
mined from the builders, 56 documented-but-unread, 16 dynamic families; 82
exposed as annotation attributes). The annotations, the binding table the
processor reads back, the guide's table, the simulator's editor schema and the
agent reference are all generated from it. The guide's table goes from 208 rows
to 529 with no prose lost.
Enums are emitted only where the accepted set is demonstrable from the code that
reads the hint -- `HardeningPreflight` rejects an unknown `harden.level`,
`IOSDependencyManager` throws on an unknown `ios.dependencyManager`, and
`GenerateDesktopAppWrapperMojo` silently falls back to `native` on an unknown
`desktop.titleBar`, which is the failure this removes.
Generated projects
------------------
The archetype and all four initializr templates now carry the annotations, and
`cn1:migrate-build-hints` moves an existing project over. Eleven in-repo
projects are migrated. `java.version` deliberately stays in the properties file:
it picks the toolchain that compiles the class the annotations live on.
Gates
-----
`scripts/check-build-hint-catalog.sh` fails when code reads a hint the catalog
does not describe, and when our own docs or templates name one that no builder
reads. Its baseline is empty, so it is a hard gate rather than a ratchet.
`scripts/gen-build-hint-annotations.sh --check` fails on generated-file drift.
Both run in the Java 8 leg of PR CI.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:8d2cfcfde3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadmaven/pom.xml
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs[Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • build-hint-catalog: 0 findings (no issues)
    • build-hint-tools: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

`.gitignore` carries a repo-wide `**/build/*`. The catalog's package is
`com.codename1.build.shared`, so all 13 of its sources sat under a path segment
named `build` and `git add` silently skipped them. Only `pom.xml` was committed:
the module built locally from the working tree and produced an empty jar in CI,
which is why `codenameone-maven-plugin` then failed with `cannot find symbol` on
`BuildHints` and nearly every job went red.
The sibling `platform-feature-catalog` lives in the same package and is fine,
because it was added before that rule existed -- tracked files stay tracked, so
nothing ever pointed at the hazard.
Un-ignore `build` when it is a Java package rather than a build output
directory, with the rationale beside the rule so the next file added there is
not lost the same way. `maven/core/build/*` and `CodenameOne/build/*` stay
ignored.
Also from review:
- Every bare `open()` in the four Python scripts now uses a context manager, so
the handle closes even if parsing or `json.dump` raises, and the writes state
their encoding.
- The generator no longer emits an IP literal as an annotation default. PMD
reads `default "127.0.0.1"` as hardcoded configuration, and the default clause
is documentation only -- the processor emits a hint solely for members the
developer actually wrote -- so the value moves to the javadoc where it belongs.
- Files the migration touched that never carried a copyright header now have the
complete one. The archetype's `__mainName__.java` is excluded instead: it is a
template for the user's own application class, and stamping a Codename One GPL
header onto it would put our licence on their code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Pin generated projects to a version containing the annotations

The updated Initializr starter archives now import and use com.codename1.annotations.buildhints, but GeneratorModel.java:44 still generates projects pinned to CN1 7.0.267, whose core artifact predates this package. Consequently every newly generated barebones, Kotlin, Grub, or Tweet project fails compilation on the unresolved annotations unless the user manually changes the CN1 version; either defer these template changes or update the generated runtime/plugin version to the first release containing them.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The archived bootstrap ran generation at module scope, so gen_external.py's
`import gen_catalog` -- which only wants three helper functions -- rewrote every
catalog source as a side effect. Generation and its diagnostics now live in
`main()` behind a `__main__` guard, and the module-level file reads became
`load_license()` / `load_mined()` / `load_docs()`, so importing does no I/O and
cannot fail on inputs the archived copy deliberately does not carry.
Verified both directions: importing leaves the catalog untouched, and running
the two scripts end to end still reproduces the committed catalog byte for byte.
Also drops `json` and `subprocess` from check-build-hint-catalog.py. Both were
left from an earlier version that shelled out to the miner instead of importing
it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:d727c7d976

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadscripts/gen-build-hint-annotations.sh Outdated
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendscalar fallback (no native SIMD)
SIMD int-add (64K x300)java 196ms / native 73ms = 2.6x speedup
SIMD float-mul (64K x300)java 144ms / native 89ms = 1.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode83.000 ms
Base64 CN1 decode86.000 ms
Base64 native encode391.000 ms
Base64 encode ratio (CN1/native)0.212x (78.8% faster)
Base64 native decode310.000 ms
Base64 decode ratio (CN1/native)0.277x (72.3% faster)
Image encode benchmark statusskipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 60ms / native 4ms = 15.0x speedup
SIMD float-mul (64K x300)java 80ms / native 3ms = 26.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode190.000 ms
Base64 CN1 decode381.000 ms
Base64 SIMD encode99.000 ms
Base64 encode ratio (SIMD/CN1)0.521x (47.9% faster)
Base64 SIMD decode91.000 ms
Base64 decode ratio (SIMD/CN1)0.239x (76.1% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)22.000 ms
Image createMask (SIMD on)15.000 ms
Image createMask ratio (SIMD on/off)0.682x (31.8% faster)
Image applyMask (SIMD off)71.000 ms
Image applyMask (SIMD on)35.000 ms
Image applyMask ratio (SIMD on/off)0.493x (50.7% faster)
Image modifyAlpha (SIMD off)44.000 ms
Image modifyAlpha (SIMD on)22.000 ms
Image modifyAlpha ratio (SIMD on/off)0.500x (50.0% faster)
Image modifyAlpha removeColor (SIMD off)34.000 ms
Image modifyAlpha removeColor (SIMD on)25.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.735x (26.5% faster)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 63ms / native 5ms = 12.6x speedup
SIMD float-mul (64K x300)java 63ms / native 4ms = 15.7x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode327.000 ms
Base64 CN1 decode216.000 ms
Base64 SIMD encode176.000 ms
Base64 encode ratio (SIMD/CN1)0.538x (46.2% faster)
Base64 SIMD decode132.000 ms
Base64 decode ratio (SIMD/CN1)0.611x (38.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)28.000 ms
Image createMask (SIMD on)23.000 ms
Image createMask ratio (SIMD on/off)0.821x (17.9% faster)
Image applyMask (SIMD off)58.000 ms
Image applyMask (SIMD on)56.000 ms
Image applyMask ratio (SIMD on/off)0.966x (3.4% faster)
Image modifyAlpha (SIMD off)66.000 ms
Image modifyAlpha (SIMD on)28.000 ms
Image modifyAlpha ratio (SIMD on/off)0.424x (57.6% faster)
Image modifyAlpha removeColor (SIMD off)44.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.727x (27.3% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 55ms / native 3ms = 18.3x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode268.000 ms
Base64 CN1 decode152.000 ms
Base64 SIMD encode66.000 ms
Base64 encode ratio (SIMD/CN1)0.246x (75.4% faster)
Base64 SIMD decode64.000 ms
Base64 decode ratio (SIMD/CN1)0.421x (57.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)13.000 ms
Image createMask (SIMD on)9.000 ms
Image createMask ratio (SIMD on/off)0.692x (30.8% faster)
Image applyMask (SIMD off)24.000 ms
Image applyMask (SIMD on)20.000 ms
Image applyMask ratio (SIMD on/off)0.833x (16.7% faster)
Image modifyAlpha (SIMD off)17.000 ms
Image modifyAlpha (SIMD on)12.000 ms
Image modifyAlpha ratio (SIMD on/off)0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off)21.000 ms
Image modifyAlpha removeColor (SIMD on)13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.619x (38.1% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

Three separate gates rejected generated output. Each is fixed in the generator
so the class of problem cannot come back through a catalog edit.
Unmappable characters. The prose is imported from the developer guide, which
uses typographic punctuation, and `CodenameOne/src` is also compiled by an Ant
javac step with ASCII encoding where a single em dash is
`error: unmappable character for encoding ASCII` -- a build failure, not a
warning. A Unicode escape would not have helped: javac expands `\uXXXX` before
it strips comments, so the character reappears. `toAscii` now folds the
punctuation that actually occurs, and *refuses* anything it has no mapping for
rather than dropping it, because silently deleting a character from a hint's
documentation is the worse outcome.
Broken table. `ios.spm.packages` is documented as `identity|url|requirement`,
and a bare `|` starts a new AsciiDoc cell, so asciidoctor reported "dropping
cells from incomplete row" for the whole 529-row table. Cells are escaped now.
Vale. The guide enforces the Microsoft style as errors, and the generated table
feeds it, so the catalog's prose has to satisfy it too: contractions, no
"and so on", no stray adverbs. A default value is not prose, though -- the one
remaining hit was `android.file_paths`, whose default is an XML fragment -- so
a quoted default now carries the `// vale-skip:` comment .vale.ini documents
for individual false positives.
Also fixes a data bug the guide exposed. The miner preserved Java escape
sequences instead of decoding them, so `android.file_paths` and
`android.facebook_permissions` recorded defaults containing literal
backslashes that the build never sees, and those reached the rendered table.
The miner decodes escapes and re-quotes safely, and the two catalog entries are
corrected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/build_hint_miner.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:96bff9038a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

`killedThreadReportsItselfFinished` failed the Java 21 leg with
"FormTest timed out after 5000ms; edt=initialized pendingSerialCalls=0". The
waits in this class used a 5000ms deadline, which is exactly the `@FormTest`
timeout in EDTTestInterceptor -- so on a loaded runner the poll loop consumed
the entire harness budget and the interceptor fired first. The report then said
only that the method timed out, with nothing about which condition never became
true.
The waits now use 2000ms, well inside the harness budget and still roughly two
thousand times the ~1ms these threads actually take to stop. A genuine
regression now fails on the test's own assertion, which names what went wrong.
Pre-existing (the test arrived with #5526) and unrelated to the build hint work:
core-unittests has no dependency on the JavaSE port, so none of the simulator
registration in this branch runs there, this branch changes nothing under
com.codename1.db or EasyThread, and the Java 8 leg passed the same commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:a343fe3335

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

A mojo's defaultPhase does not bind it to a project -- the project's POM has to
-- and nothing turns a build hint annotation back into a codename1.arg.* pair
except the process-annotations goal. So migrating a project without that
binding deleted working properties and replaced them with annotations no goal
ever reads: the hints vanished from the build with no diagnostic anywhere.
Five projects in this branch were already in that state. gamebuilder,
docs/demos, video-builder and cn1playground bind the plugin but not that goal,
so the binding is added. input-validation-app's common module has no build
section at all, so its migration is reverted rather than inventing a lifecycle
for a demo app.
The goal now checks the reactor for the binding and refuses with the execution
block to paste, so this cannot happen to anyone else.
Three more from the same review:
- The deletion pass recognized only `key=value`. `Properties.load` also accepts
`key:value`, `key value`, escaped separators inside the key, and logical
continuation lines; a declaration it failed to match was left behind while the
annotation was added, so the next build failed with the duplicate-hint error
this goal exists to prevent. Keys are parsed the way Properties.load defines
them now, with a unit test per form.
- The settings file was read as ISO-8859-1 and written back as UTF-8, turning
any unrelated non-ASCII byte -- an accented displayName, say -- into mojibake.
It is written back as ISO-8859-1.
- cn1.androidTheme and cn1.nativeTheme are deprecated aliases of and.themeMode
and nativeTheme, which the builders honour as fallbacks. Neither declared
aliasOf, so conflict detection missed them and one value silently won.
Also: the generation script rebuilt the generator only when its class was
absent, so editing a catalog source and rerunning regenerated every view from
the previous build's bytecode -- reporting success while ignoring the edit, and
passing --check on a tree that was genuinely stale. It always rebuilds now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:0edef42ca4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…plicating a hint
docs/demos is the developer guide's snippet project: deliberately incomplete
code fragments that illustrate @entity, @route, @AppIntent and @mapped. Binding
process-annotations there put those snippets in front of the other processors,
which correctly rejected six of them, so the migration is reverted and its two
hints are back in the properties file. That the project omitted the goal was the
point, not an oversight.
The other three newly bound projects were checked rather than assumed:
gamebuilder, video-builder and cn1playground each run process-annotations
cleanly and emit 6, 3 and 5 hints respectively.
Settings could still create the duplicate the migration is careful to avoid. In
a generated project ios.themeMode and its neighbours are annotations, but the
Build Hints UI decides a hint is inactive from the properties file alone and its
Add button writes a property -- producing a second declaration that fails the
next build. The tool now reads META-INF/codenameone/build-hints.properties, the
file the processor writes on every build and deletes when the last annotation
goes, and renders those hints read-only with the attribute that owns them:
"Set by @iOS(themeMode) on the main class." An unbuilt project has no such file
and behaves as before.
Also fixes the SpotBugs finding this branch introduced: `backslashes % 2 == 1`
in the continuation scan is false for negative odd numbers, so it is `!= 0`.
The count cannot go negative, but the idiom is wrong regardless of that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Keep Initializr templates compatible with the pinned runtime

Every Initializr source archive now imports com.codename1.annotations.buildhints and uses the new annotations, while GeneratorModel.CN1_PLUGIN_VERSION still rewrites generated projects to 7.0.267, whose codenameone-core predates that package; the generated common POM also omits process-annotations. Consequently all newly downloaded Initializr projects fail compilation instead of receiving the defaults removed from common.zip's settings file. Leave these templates property-backed until Initializr targets the release containing this feature, or bump the generated version and bind the processor.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 339 seconds

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 62ms / native 3ms = 20.6x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathactive (NEON-accelerated)
Base64 CN1 encode167.000 ms
Base64 CN1 decode120.000 ms
Base64 native encode624.000 ms
Base64 encode ratio (CN1/native)0.268x (73.2% faster)
Base64 native decode231.000 ms
Base64 decode ratio (CN1/native)0.519x (48.1% faster)
Base64 SIMD encode56.000 ms
Base64 encode ratio (SIMD/CN1)0.335x (66.5% faster)
Base64 SIMD decode45.000 ms
Base64 decode ratio (SIMD/CN1)0.375x (62.5% faster)
Base64 encode ratio (SIMD/native)0.090x (91.0% faster)
Base64 decode ratio (SIMD/native)0.195x (80.5% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)6.000 ms
Image createMask (SIMD on)2.000 ms
Image createMask ratio (SIMD on/off)0.333x (66.7% faster)
Image applyMask (SIMD off)46.000 ms
Image applyMask (SIMD on)36.000 ms
Image applyMask ratio (SIMD on/off)0.783x (21.7% faster)
Image modifyAlpha (SIMD off)39.000 ms
Image modifyAlpha (SIMD on)39.000 ms
Image modifyAlpha ratio (SIMD on/off)1.000x (0.0% slower)
Image modifyAlpha removeColor (SIMD off)47.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.681x (31.9% faster)

Every project the archetype and the initializr produce is pinned to a released
Codename One version -- the initializr hardcodes 7.0.267 in
GeneratorModel.CN1_PLUGIN_VERSION -- and no released core carries
com.codename1.annotations.buildhints. So a generated project would import
annotations that do not resolve and fail to compile before the user has written
a line, and the settings those templates stopped declaring would simply be gone.
The templates are reverted to exactly their previous state: the archetype's
__mainName__.java and codenameone_settings.properties, and the initializr's
common.zip and four source archives. They can move to annotations in a follow-up
once a release containing the package is out.
The generated build hint table is dropped from the agent skill reference for the
same reason -- it documented a form those projects cannot use yet -- so the
generator no longer rewrites markdown at all.
What stays from that area is unrelated to annotations: the skill reference
described build hints that no builder reads, so a reader copying them got a
green build and no effect. android.xPermissions is spelled android.xpermissions,
android.minSdkVersion is android.min_sdk_version, and android.sdkVersion,
android.googlePlayVersion, build.compile, build.timeout, javascript.html5,
javascript.bundleResources and ios.orientation do not exist at all. Those
corrections are right for the published version too, and the catalog gate now
holds our own documentation to them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
compilesJava() already withheld the conventional src/main/java from the search
when the POM chain switches default-compile off with <phase>none</phase> and
binds nothing in its place. <sourceDirectory> is the same root, declared instead
of assumed, and it was not gated -- so a module that compiles nothing with javac
still offered it, and a stale copy of the main class there answered ahead of the
compiled source.
Per element rather than over the whole list, because a Kotlin-only module is
exactly the case where javac does not run and its <sourceDirs> must still be
searched. Both directions are asserted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:705783d9b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/release-on-maven-central.yml
shai-almogand others added 10 commits August 27, 2026 11:08
…writes
The .java branch in write() guarded against a non-ASCII character reaching a
generated Java source; the generator writes a JSON data file and an asciidoc
table and nothing else. toAscii survives because the reader test still needs it
-- the catalog's prose came from the developer guide and the annotations are
ASCII Java sources, so the two cannot be compared verbatim -- and its
documentation now says that rather than describing a build step it no longer
feeds.
Also the HintGroup local in Bindings that nothing read, and the no-op continue
at the end of the loop that was its only use. toHint already resolves the group
and throws when an annotation type names none.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Central recovery poll queried the two build hint artifacts with a single
curl carrying two URLs. curl needs an -o per URL and emits --write-out after
each transfer, so the second POM body arrived on stdout with both status codes
appended: the captured value could never equal 200, and the poll -- which exists
to rescue a release Central has already accepted -- would exhaust all 90
attempts and fail it. Reported by codex.
Both this poll and the R2 confirmation walk a list of artifacts, and each
carried its own copy, which had already drifted: Central's omitted
codenameone-core. The list is now named once in the job environment and both
read it, with a guard that refuses an empty list rather than reporting success
for having checked nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aliasOf, deprecated, external, enterpriseOnly and link exist so an annotated hint
can say what the catalog entries already say, and none of the 87 shipped
attributes needs any of them -- so the reader's handling of all five was carried
by no test at all. Exercised now against a copy of the real annotation package
with one probe attribute added, which is what the reader compiles anyway.
Kept rather than deleted: the first annotated hint that has to be marked
deprecated needs somewhere to say so, and would otherwise be the first thing ever
to run that path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
validateScreenshotContent threw before the write, so a failing scenario left
nothing behind but a pixel count -- which is exactly what happened to the
component-inspector run that reported textPixels=738 with no image to look at.
The capture is now written as <name>.png.rejected.png first, into the directory
the workflow already uploads with if: always(), so the next occurrence can be
diagnosed instead of guessed at.
Not a fix for that failure: the check reads a fixed screen rectangle and the run
before and after it were green on the same commit, so what it saw is still
unknown. This is what makes finding out possible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e</phase>
compilesJava() answered from the first level in the chain that switched
default-compile off, so an ancestor that disables it decided for a child that
binds its own compile execution. A module that plainly does compile Java read as
one that does not, and its Java roots were then withheld from the search -- where
a Kotlin or stale copy of the main class answers for it instead.
An enabled compile binding anywhere in the chain now settles it, and it is looked
for before any disabling level is allowed to decide. Extracted to a static form
taking the chain directly, because the walk needed a project on disk and was the
untested half of a method whose two halves are separately covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
attributeOf was written to replace a substring search for combine.self, and then
looked for the attribute name anywhere inside the start tag -- so xcombine.self
would have answered for it. The same rule, reproduced one level down in the fix
for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…were masking
Two failures on the JDK 8 leg, one the cause of the other.
The module's POM binds cn1:css at process-classes and the CSS compiler opens a
JFrame, which in this container is java.awt.HeadlessException before a single
test runs. Run under xvfb-run, which this job already uses for the Ant build a
few steps later.
That failure then skipped every step after it, including "Run SpotBugs for
ByteCodeTranslator" -- so the quality report failed for a missing
ByteCodeTranslator report, which said nothing about what had actually broken.
The step now runs last among this leg's gates, which also keeps its reinstall of
the maven plugin away from the SpotBugs reports the earlier steps produce.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three groups, none of which any reviewer asked for and all of which I added off
my own red-team pass:
The Settings POM-parser rewrites -- depth-matched </plugin>, combine.self and
combine.children read off their own element, comment stripping in parentPomPath,
the compilesJava chain walk, and the <sourceDirectory> gate. These change how the
Settings tool parses arbitrary user POMs, on the strength of cases nobody
reported. The parser is back to what it was.
The simulator verifier's rejected-capture diagnostics, which have nothing to do
with build hints -- I added them while chasing a screenshot failure that predates
this branch.
The release workflow's shared RELEASE_ARTIFACTS list. Only the defect codex
reported is kept: the two build hint POMs are queried with one curl each, because
two URLs share the single -o and the captured value could never be 200.
The plugin-side cleanups that rode along in the same commit as the parser work --
the unused ProcessorContext constructors and LibraryHintMerger's duplicate
prefix -- are kept, as is the comment de-duplication.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A generated file in git conflicts on every merge and there is nothing for a hand
edit to survive in, so it should never have been checked in.
It cannot simply move to one module's target/classes: BuildHints.entries() loads
it, so every consumer of the catalog needs it on its own classpath, and the
generator lives in build-hint-tools which depends on the catalog -- the catalog
cannot render its own resource without a dependency cycle. So each of the three
modules that actually needs it renders it into its own target/classes at
process-classes: maven/javase, maven/codenameone-maven-plugin and
scripts/settings/common. exec:java with classpathScope=compile, because a
plugin-level dependency resolves from the repository rather than the reactor and
would be a chicken and egg on a clean checkout. build-hint-tools is provided
scope in the two application modules so ASM cannot reach a built app.
Verified: all three render byte-identically to the file that was committed, the
catalog jar no longer carries it, and the javase jar does.
Consequences elsewhere. gen-build-hint-annotations.sh renders to a scratch
directory, and --check no longer diffs against a committed copy -- there is none
-- but asserts the rendering still succeeds and produces the full set, which is
the failure that would otherwise reach a user as an editor with no hints in it.
check-build-hint-catalog.py read the committed path and returned an empty set
when it was absent, which failed the gate OPEN; it now reads whichever module has
rendered it and says to build one when none has. The Ant JavaSE build takes it
from maven/javase/target/classes when that build has run, and without it the
simulator keeps the hints BuildHintSchemaDefaults compiles in and says the
annotated ones are missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removing the committed data file broke two catalog tests, and I did not see it
because I verified that module with -DskipTests -- on the one module whose
structure I had just changed.
BuildHints.entries() is complete only on a classpath carrying the rendered data
file. The catalog module cannot render one for its own tests: the generator lives
in build-hint-tools, which depends on the catalog. So the two assertions that
need the complete set -- every alias resolves to a real hint, and the catalog
agrees with LibraryHintMerger on every separator it defines -- move to
build-hint-tools, which renders the data into its own test classes first. Both
fail without that render, which is checked.
What stays beside the catalog is what its own sources declare, and the class now
says so rather than looking like it covers everything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:9b07db8e93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/developer-guide-docs.yml
shai-almogand others added 3 commits August 27, 2026 14:05
…lows
Making the table generated instead of committed took away the trigger it used to
get for free: while it was checked in, editing it showed up as a docs diff. The
replacement path list named the catalog and the render script but not the two
inputs that matter most -- the annotations, whose javadoc is the Description
column verbatim, and build-hint-tools, which is the renderer itself. A PR
renaming an attribute or rewording its documentation could therefore change the
guide without ever running the AsciiDoc and Vale checks over the result.
Reported by codex.
Fixed in both copies of that list in developer-guide-docs.yml, since triggering
the workflow is not enough on its own -- the HTML and PDF build is gated on the
paths-filter as well, and the two had already drifted.
And in website-docs.yml, which is the other workflow that renders the same table
and had the same gap for the same reason. Its pull_request and push lists both
already covered CodenameOne/src/**, so only the catalog, the renderer and the
script were missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two files overlapped and both auto-merged: website-docs.yml keeps master's
website telemetry step alongside the build hint table paths added here, and
CLAUDE.md keeps master's restructure -- 163 lines cut and the GC notes moved to
vm/CLAUDE.md -- alongside this branch's build hints section.
That section is rewritten rather than merged as-is: it still described writing
cn1-build-hints.json into the tree and told the reader to run the generator to
"rewrite the data file". Nothing is written into the tree any more, so it now
says where the file is rendered instead, and the command list loses the step that
no longer does anything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
windows-tooling builds the Settings tool with -Dcodename1.platform=javase, which
activates a profile declaring exec-maven-plugin with its own plugin level
<arguments> containing a <classpath/> element. Maven merges plugin level
configuration into every execution of that plugin, so that Object landed in the
generator execution's String array:
Cannot store value into array: ... can not cast one of the elements of
java.lang.Object[] to the type of the destination array, java.lang.String
A plain local build never activates that profile, which is why four green local
runs said nothing. Reproduced with the property set, fixed with
combine.self="override" on the arguments, and re-verified against the exact
command the workflow runs. Applied to all four generator executions rather than
only the one that failed: the collision needs a second declaration of the same
plugin anywhere in the effective build, which is not a property of this module.
Also restores the simulator verifier's rejected capture. It was reverted as out
of scope, and then the component inspector scenario failed a second time with a
byte identical textPixels=738 -- twice the same number is a state, not a race --
with no image kept to say what was on screen. Two failures in 22 runs on this
branch and none in 38 on any other is too specific to guess at.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:2d2708a0c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almogand others added 5 commits August 27, 2026 14:50
BytecodeComplianceMojo rewrites class files in place -- capping a class to the
supported version, and redirecting a call the runtime does not have. The build
hint manifest records the main class's own bytes, because the simulator has no
bytecode reader and can only compare the class file itself. If the main class is
one of the rewritten ones, a manifest written before that goal describes a class
that no longer exists on disk, and the simulator reads a manifest generated
moments earlier as stale and publishes none of the annotated hints. Reported by
codex.
Every pom in this repository happens to run process-annotations after this goal,
where the stamp is taken from the rewritten bytes anyway, so nothing is broken
today -- I checked all twelve. Nothing enforces that order though, and the
failure mode is silent: hints disappear, no error. Re-stamping here as well makes
the order stop mattering, since whichever of the two runs last leaves a manifest
describing the class that is actually there. It is a no-op when there is no
manifest, which is every project that declares its hints in the properties file.
The test asserts the hazard and the repair together: capping a version changes
the class, which invalidates a stamp taken before it, and re-stamping restores
the match without touching the hints. Removing the re-stamp fails it. What the
test does not cover is the one-line call from executeImpl, which needs a
MavenProject to reach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…wants
Both predate this branch without one. check-copyright-headers is scoped to the
PR's diff, so editing them is what brought them into scope -- the gate working as
designed, not a new defect. They take the Codename One GPLv2 + Classpath
Exception header their siblings in this package carry.
I ran that gate locally before the last push and it reported success while
checking nothing: with no --base it has no diff to scope to, and "0 file(s)
passed" was the tell I read straight past. Run with --base origin/master it
checks the same 128 files CI does, and passes. check-cast-semantics.sh takes
--baseline, a ratchet file rather than a git ref, so it is whole-repo and running
it bare is valid; copyright was the only gate here with that shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guide said the compiler-checked form existed and the generated table named
the annotation for each hint, but nothing anywhere showed what one looks like. A
reader had the Annotation column and no way to turn `@Ios(pods)` into code.
Adds a worked example and the exact properties lines it replaces, then explains
the three things about the syntax that are not guessable from the table: a list
hint takes a Java array and the build joins it with that hint's own separator, a
boolean hint takes Toggle rather than boolean so that leaving it out means the
build decides, and a hint with a closed value set takes an enum. Also says
plainly that the long tail and the open-ended families stay in the properties
file and that the two forms mix freely.
Every mapping in the example is checked against the generated data rather than
written from memory: each attribute resolves to the hint name claimed, each enum
constant to the wire value claimed, and ios.pods really does join with a comma.
The Java snippet compiles against the annotations, the guide's snippet validator
passes (inline blocks are refused there, so both snippets live in docs/demos),
Vale is clean, and the chapter renders without warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LanguageTool fails the developer guide build on a single match, and I had
hyphenated it in one sentence and not the other. Caught by running the gate
locally on the rendered chapter rather than by a CI cycle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A new .java file, so the diff-scoped gate checks it. I ran that gate with a base
earlier and it passed, then added this file and pushed without re-running it --
which is exactly what my own note about running diff-scoped gates AFTER the
commit is for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6fd1406078

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadPorts/JavaSE/src/com/codename1/impl/javase/Simulator.java
A project that adds @build or @DesktopBuild to its main class but never binds
cn1:process-annotations -- an upgraded or hand-written POM -- compiles and
launches fine, emits no manifest, and the simulator returned quietly and applied
none of the annotated hints. CN1BuildMojo refuses a device build for exactly this
case, so the simulator was the one place where the hints vanished with nothing
said and local behaviour diverged from device behaviour for the same project.
Reported by codex.
It now says so. A warning rather than a refusal: the simulator's job is to start,
and what it is missing are build settings, not something it cannot run without.
Detected by scanning the main class file's bytes for the annotations package,
because the simulator has no bytecode reader and an annotation's type is in the
constant pool as a descriptor. That is wider than reading the annotation table --
a main class that merely mentions the package matches too -- which is why this
warns and does not refuse.
Three cases are asserted: annotated with no manifest warns and names both the
class and the goal, an unannotated main class is silent, and a main class absent
from the classpath is silent. The first fails if the marker check is removed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:b0f5213ef4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import com.codename1.annotations.buildhints.Toggle;

// tag::buildHintAnnotations[]
@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the documentation snippet out of application bytecode

When docs/demos is built for a device through its reactor, this source is compiled into the common module even though codenameone_settings.properties names com.codenameone.developerguide.DemoCode as the main class. CN1BuildMojo.failOnMisplacedAnnotations() rejects live build-hint annotations on every non-main class, so BuildHintAnnotationSnippet makes that build fail before submission; binding process-annotations would instead fail during process-classes. Store the include outside the compiled source root or render it from a noncompiled snippet.

Useful? React with 👍 / 👎.

The developer guide's quality gate counts Vale alerts at suggestion level and
fails on any of them. I checked the chapter locally with --minAlertLevel=error,
which hides warnings, so two Microsoft.Adverbs warnings -- "deliberately" and
"freely" -- went through and reddened the build. Both sentences say the same
thing without the adverb.
Re-checked at the level CI uses: Vale 0/0/0, LanguageTool 0 matches, snippet
validator 706 blocks, paragraph capitalization clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog
shai-almog merged commit d21a389 into masterAug 27, 2026
72 checks passed
@shai-almog
shai-almog deleted the build-hint-annotations branch August 27, 2026 17:13
shai-almog added a commit that referenced this pull request Aug 27, 2026
Two conflicts. .gitignore had a new entry on each side and keeps both.
Advanced-Topics-Under-The-Hood.asciidoc is the one that mattered: master's #5586
replaced the hand-written build hint table with an include generated from
maven/build-hint-catalog, while this branch had added 28 macos.* rows to that
table. Resolving to master's include alone would have compiled cleanly and
silently deleted the documentation for every macOS build hint the port added --
the table is generated now, so a hint absent from the catalog has no
documentation anywhere.
So the 28 hints move into BuildHintsApple with the descriptions they had in the
table, and the generated table carries all 28 again. Confirmed by running
scripts/gen-build-hint-table.sh and counting them in the output, not by reading
the diff.
macNative.iosMinDeploymentTarget was already in the catalog and is NOT
duplicated; the first pass nearly added it back because a name pattern stopped
at the underscore in macos.add_libs and mis-parsed that row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Aug 27, 2026
master's #5586 made annotations the source for build hints that have one, and
the merge landed this port's 28 macos.* hints as hand-written catalog entries --
in HintGroup.MAC_NATIVE, whose key prefix is macNative. and which carries no
annotation at all. They belonged in the feature, not beside it.
HintGroup gains MAC_OS("Mac", "macos."), and @Mac declares all 28 as
compile-checked attributes: Toggle for the nine booleans, an appendable String[]
for addLibs following the ios.add_libs shape, and an explicit name for the
fifteen whose tail has dots and cannot be derived from a method name. The
hand-written entries are gone, because two sources for one hint is the drift
this feature exists to remove. macNative.* stays hand written: it is the
spelling the legacy Catalyst target reads and no annotation offers it.
Verified by regenerating rather than by reading the diff -- 28 macos rows in the
guide table, macos.entitlements.appSandbox typed boolean, and the catalog and
tools tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@shai-almog
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Check build hints at compile time instead of shipping them inert - #5586

Merged
shai-almog merged 244 commits into
masterfrom
build-hint-annotations
Aug 27, 2026
Merged

Check build hints at compile time instead of shipping them inert#5586
shai-almog merged 244 commits into
masterfrom
build-hint-annotations

Conversation

@shai-almog

@shai-almogshai-almog commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

The problem

A build hint is a codename1.arg.<name>=<value> line that reaches a builder as request.getArg(name, default). Nothing checked the name, so a misspelling was accepted, copied into the build request, never read, and silently discarded — a green build with the setting simply not applied.

Our own agent reference had been shipping keys in exactly that state:

Documented in skill/references/build-hints.mdActually read by the builders
android.xPermissionsandroid.xpermissions (AndroidGradleBuilder.java:1206)
android.minSdkVersionandroid.min_sdk_version
android.sdkVersion(nothing — android.targetSDKVersion is the real one)

The change

87 hints can now be written as annotations on the application's main class, where javac does the checking: a misspelled name is an unknown symbol, a wrong value type is a type error, and a value outside a hint's supported set is an unknown enum constant.

@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)
@Build(nativeTheme = ThemeMode.MODERN)
@DesktopBuild(titleBar = DesktopTitleBar.NATIVE, width = 1280, height = 800)
@Ios(themeMode = ThemeMode.MODERN, newStorageLocation = Toggle.ON)
publicclassMyApplicationextendsLifecycle {
}

Seven annotations — @Ios, @Android, @DesktopBuild, @Build, @Hardening, @IosPrivacy, @OnDeviceDebug — and ten enums.

No attribute has a default that means anything

Every default is a marker for "nothing was said": Toggle.DEFAULT, "", {}, 0, or an enum's @HintUnset constant. An attribute left out is absent from the class file, so the processor emits nothing for it and the build decides, exactly as it does for a hint nobody wrote.

This is the reason there are no boolean attributes left. boolean appBundle() default false reads as "off unless you turn it on" while AndroidGradleBuilder defaults android.appBundle to true — and a copy of the server's answer compiled into every app already built cannot follow the server when it changes. Toggle is the three-state replacement, and a test refuses any hint attribute that declares a value-bearing default.

The builders are untouched.BuildHintAnnotationProcessor converts the annotations back into the same key/value pairs, and CN1BuildMojo merges them before the command-line overlay, the CN1Lib merges and both preflights — so a library still appends onto an annotation-supplied value and -D still wins. Simulator publishes them as system properties at startup so cn1:run sees hints that no longer live in the properties file.

The properties file is untouched too. It stays the way to set the long tail and the open-ended families such as android.permission.<NAME> that an annotation cannot express, with no new warnings or errors on that path. Declaring one hint both ways is a build error.

Where a hint is declared

Exactly one of two places:

  • CodenameOne/src/com/codename1/annotations/buildhints if it has an annotation. These are hand-written and are the source of truth for the hints they expose; BuildHintAnnotationReader reads them back with ASM rather than any file restating them.
  • maven/build-hint-catalog otherwise — dynamic families, build-service-only hints, the long tail.

The hint set used to be described in five places that had drifted apart: a prose table in the developer guide, a runtime scraper of that table in the Settings tool that guessed each type by string-matching the description, a fifteen-entry schema in the simulator, a fourteen-entry separator map in the plugin, and a hand-written agent reference. Only 147 of ~520 names appeared in more than one.

Nothing is generated into the tree

No generated file is committed. BuildHintCodeGenerator renders two views, both during a build:

  • cn1-build-hints.json for the two editors that are Codename One apps and so have no bytecode reader — the Settings tool and the simulator's hint editor. Each module that needs it renders it into its own target/classes (maven/javase, maven/codenameone-maven-plugin, scripts/settings/common). The catalog cannot render its own, because the generator lives in build-hint-tools, which depends on the catalog.
  • the developer guide's table, rendered when the guide is built.

Anything that can read bytecode reads the annotations directly and never touches the data file.

The guide's table goes from 208 rows to 570 with no prose lost, and gains Type, Default and Annotation columns it never had. An annotated hint's Default reads "set by the build" rather than a value, which is the honest statement of the paragraph above.

Enums are emitted only where the accepted set is demonstrable from the code that reads the hint — HardeningPreflight rejects an unknown harden.level, IOSDependencyManager throws on an unknown ios.dependencyManager, and GenerateDesktopAppWrapperMojo silently falls back to native on an unknown desktop.titleBar, which is precisely the failure this removes.

Scope: generated projects are deliberately not migrated here

Every project the archetype and the initializr produce is pinned to a released Codename One version — the initializr hardcodes 7.0.267 in GeneratorModel.CN1_PLUGIN_VERSION — and no released core carries com.codename1.annotations.buildhints. A generated project would import annotations that do not resolve and fail to compile before the user has written a line.

So the archetype's __mainName__.java and codenameone_settings.properties, and the initializr's common.zip and four source archives, are unchanged. They move to annotations in a follow-up once a release containing the package is out. scripts/skindesigner (7.0.255) stays on properties for the same reason, and cn1:migrate-build-hints refuses any project whose core lacks the package.

The in-repo tool projects that build against the snapshot from source are migrated: gamebuilder, video-builder, cn1playground, certificatewizard, guibuilder, fidelity-app, purchase-test-app, settings, hellocodenameone. protocol-e2e is not in that list: its only hint is codename1.arg.java.version, which has to stay in the properties file because the compiler needs it in order to compile the class that would otherwise declare it.

docs/demos is deliberately excluded: it is the developer guide's snippet project, full of intentionally incomplete fragments, and running the annotation processors over it fails by design.

Settings tool

It no longer scrapes the guide's AsciiDoc and guesses types; it reads the catalog. It also validates closed value domains, and refuses to edit a hint an annotation already owns — reading META-INF/codenameone/build-hints.properties and showing "Set by @Ios(themeMode) on the main class" — because writing a property for such a hint would create the duplicate declaration that fails the next build.

Its own tests had never run anywhere: both workflows that touched the module passed -Dmaven.test.skip=true, so every test written for the POM reader and the hint editor was dead weight. They now run in PR CI, 161 of them.

Gates

  • scripts/check-build-hint-catalog.sh fails when code reads a hint the catalog does not describe, and when our own docs or project templates name one that no builder reads. Its baseline is empty, so it is a hard gate rather than a ratchet. 497 hints read by the builders, all described.
  • scripts/gen-build-hint-annotations.sh --check asserts the render still succeeds and produces the full set. It is not a drift check — there is no committed copy to drift from — but a render that silently produced nothing would otherwise reach a user as an editor with no hints in it.
  • A JUnit suite checks the catalog's own consistency (attribute-name legality including the JLS 9.6.1 Object/Annotation method-name rule, enum domains, separators matching what LibraryHintMerger used to define), and build-hint-tools asserts the invariants that need the complete hint set, since that only exists on a classpath carrying the rendered data.
  • BytecodeComplianceMojo re-stamps the manifest after its in-place class rewrites, so the order of process-annotations against it stops mattering.
  • The developer-guide and website workflows list every input the table is rendered from — the annotations, the catalog, the renderer and the script — so a reworded attribute cannot change published documentation without running the AsciiDoc and Vale checks.

A build hint is a `codename1.arg.<name>=<value>` line that reaches a builder as
`request.getArg(name, default)`. Nothing checked the name, so a misspelling was
accepted, copied into the build request, never read, and silently discarded: a
green build with the setting simply not applied.
Our own agent reference had been shipping `android.xPermissions`,
`android.minSdkVersion` and `android.sdkVersion` for exactly that reason. The
builders read `android.xpermissions`, `android.min_sdk_version`, and nothing at
all.
Most hints can now be written as annotations on the application's main class,
where javac does the checking: a misspelled name is an unknown symbol, a wrong
value type is a type error, and a value outside a hint's supported set is an
unknown enum constant.
@iOS(newStorageLocation = true, themeMode = IosThemeMode.MODERN)
@android(minSdkVersion = 24, useAndroidX = true)
@desktop(titleBar = DesktopTitleBar.NATIVE)
public class MyApplication extends Lifecycle {
}
The builders are untouched: `BuildHintAnnotationProcessor` converts the
annotations back into the same key/value pairs and `CN1BuildMojo` merges them
before the command-line overlay, the CN1Lib merges and both preflights, so a
library still appends onto an annotation-supplied value and `-D` still wins.
`Simulator` publishes them as system properties at startup so `cn1:run` sees
hints that no longer live in the properties file.
The properties file is untouched too. It stays the way to set the long tail and
the open-ended families such as `android.permission.<NAME>` that an annotation
cannot express, with no new warnings or errors. Declaring one hint both ways is
a build error.
One catalog, five generated views
---------------------------------
The hint set had been described in five places that had drifted apart: a prose
table in the developer guide, a runtime scraper of that table in the Settings
tool that guessed each type by string-matching the description, a fifteen-entry
schema in the simulator, a fourteen-entry separator map in the plugin, and a
hand-written agent reference. Only 147 of ~520 names appeared in more than one.
`maven/build-hint-catalog` is now the single source of truth (529 hints: 457
mined from the builders, 56 documented-but-unread, 16 dynamic families; 82
exposed as annotation attributes). The annotations, the binding table the
processor reads back, the guide's table, the simulator's editor schema and the
agent reference are all generated from it. The guide's table goes from 208 rows
to 529 with no prose lost.
Enums are emitted only where the accepted set is demonstrable from the code that
reads the hint -- `HardeningPreflight` rejects an unknown `harden.level`,
`IOSDependencyManager` throws on an unknown `ios.dependencyManager`, and
`GenerateDesktopAppWrapperMojo` silently falls back to `native` on an unknown
`desktop.titleBar`, which is the failure this removes.
Generated projects
------------------
The archetype and all four initializr templates now carry the annotations, and
`cn1:migrate-build-hints` moves an existing project over. Eleven in-repo
projects are migrated. `java.version` deliberately stays in the properties file:
it picks the toolchain that compiles the class the annotations live on.
Gates
-----
`scripts/check-build-hint-catalog.sh` fails when code reads a hint the catalog
does not describe, and when our own docs or templates name one that no builder
reads. Its baseline is empty, so it is a hard gate rather than a ratchet.
`scripts/gen-build-hint-annotations.sh --check` fails on generated-file drift.
Both run in the Java 8 leg of PR CI.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:8d2cfcfde3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadmaven/pom.xml
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/build_hint_miner.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_external.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs[Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • build-hint-catalog: 0 findings (no issues)
    • build-hint-tools: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

`.gitignore` carries a repo-wide `**/build/*`. The catalog's package is
`com.codename1.build.shared`, so all 13 of its sources sat under a path segment
named `build` and `git add` silently skipped them. Only `pom.xml` was committed:
the module built locally from the working tree and produced an empty jar in CI,
which is why `codenameone-maven-plugin` then failed with `cannot find symbol` on
`BuildHints` and nearly every job went red.
The sibling `platform-feature-catalog` lives in the same package and is fine,
because it was added before that rule existed -- tracked files stay tracked, so
nothing ever pointed at the hazard.
Un-ignore `build` when it is a Java package rather than a build output
directory, with the rationale beside the rule so the next file added there is
not lost the same way. `maven/core/build/*` and `CodenameOne/build/*` stay
ignored.
Also from review:
- Every bare `open()` in the four Python scripts now uses a context manager, so
the handle closes even if parsing or `json.dump` raises, and the writes state
their encoding.
- The generator no longer emits an IP literal as an annotation default. PMD
reads `default "127.0.0.1"` as hardcoded configuration, and the default clause
is documentation only -- the processor emits a hint solely for members the
developer actually wrote -- so the value moves to the javadoc where it belongs.
- Files the migration touched that never carried a copyright header now have the
complete one. The archetype's `__mainName__.java` is excluded instead: it is a
template for the user's own application class, and stamping a Codename One GPL
header onto it would put our licence on their code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/check-build-hint-catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed
Comment threadtools/build-hint-bootstrap/gen_catalog.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Pin generated projects to a version containing the annotations

The updated Initializr starter archives now import and use com.codename1.annotations.buildhints, but GeneratorModel.java:44 still generates projects pinned to CN1 7.0.267, whose core artifact predates this package. Consequently every newly generated barebones, Kotlin, Grub, or Tweet project fails compilation on the unresolved annotations unless the user manually changes the CN1 version; either defer these template changes or update the generated runtime/plugin version to the first release containing them.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The archived bootstrap ran generation at module scope, so gen_external.py's
`import gen_catalog` -- which only wants three helper functions -- rewrote every
catalog source as a side effect. Generation and its diagnostics now live in
`main()` behind a `__main__` guard, and the module-level file reads became
`load_license()` / `load_mined()` / `load_docs()`, so importing does no I/O and
cannot fail on inputs the archived copy deliberately does not carry.
Verified both directions: importing leaves the catalog untouched, and running
the two scripts end to end still reproduces the committed catalog byte for byte.
Also drops `json` and `subprocess` from check-build-hint-catalog.py. Both were
left from an earlier version that shelled out to the miner instead of importing
it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:d727c7d976

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadscripts/gen-build-hint-annotations.sh Outdated
@github-actions

github-actionsBot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.03% (8939/99006 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46203/523317), branch 3.45% (1709/49575), complexity 3.45% (1825/52883), method 5.30% (1475/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendscalar fallback (no native SIMD)
SIMD int-add (64K x300)java 196ms / native 73ms = 2.6x speedup
SIMD float-mul (64K x300)java 144ms / native 89ms = 1.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode83.000 ms
Base64 CN1 decode86.000 ms
Base64 native encode391.000 ms
Base64 encode ratio (CN1/native)0.212x (78.8% faster)
Base64 native decode310.000 ms
Base64 decode ratio (CN1/native)0.277x (72.3% faster)
Image encode benchmark statusskipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 60ms / native 4ms = 15.0x speedup
SIMD float-mul (64K x300)java 80ms / native 3ms = 26.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode190.000 ms
Base64 CN1 decode381.000 ms
Base64 SIMD encode99.000 ms
Base64 encode ratio (SIMD/CN1)0.521x (47.9% faster)
Base64 SIMD decode91.000 ms
Base64 decode ratio (SIMD/CN1)0.239x (76.1% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)22.000 ms
Image createMask (SIMD on)15.000 ms
Image createMask ratio (SIMD on/off)0.682x (31.8% faster)
Image applyMask (SIMD off)71.000 ms
Image applyMask (SIMD on)35.000 ms
Image applyMask ratio (SIMD on/off)0.493x (50.7% faster)
Image modifyAlpha (SIMD off)44.000 ms
Image modifyAlpha (SIMD on)22.000 ms
Image modifyAlpha ratio (SIMD on/off)0.500x (50.0% faster)
Image modifyAlpha removeColor (SIMD off)34.000 ms
Image modifyAlpha removeColor (SIMD on)25.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.735x (26.5% faster)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 63ms / native 5ms = 12.6x speedup
SIMD float-mul (64K x300)java 63ms / native 4ms = 15.7x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode327.000 ms
Base64 CN1 decode216.000 ms
Base64 SIMD encode176.000 ms
Base64 encode ratio (SIMD/CN1)0.538x (46.2% faster)
Base64 SIMD decode132.000 ms
Base64 decode ratio (SIMD/CN1)0.611x (38.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)28.000 ms
Image createMask (SIMD on)23.000 ms
Image createMask ratio (SIMD on/off)0.821x (17.9% faster)
Image applyMask (SIMD off)58.000 ms
Image applyMask (SIMD on)56.000 ms
Image applyMask ratio (SIMD on/off)0.966x (3.4% faster)
Image modifyAlpha (SIMD off)66.000 ms
Image modifyAlpha (SIMD on)28.000 ms
Image modifyAlpha ratio (SIMD on/off)0.424x (57.6% faster)
Image modifyAlpha removeColor (SIMD off)44.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.727x (27.3% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 163 screenshots: 163 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 55ms / native 3ms = 18.3x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 native bridgeunavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathgated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode268.000 ms
Base64 CN1 decode152.000 ms
Base64 SIMD encode66.000 ms
Base64 encode ratio (SIMD/CN1)0.246x (75.4% faster)
Base64 SIMD decode64.000 ms
Base64 decode ratio (SIMD/CN1)0.421x (57.9% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)13.000 ms
Image createMask (SIMD on)9.000 ms
Image createMask ratio (SIMD on/off)0.692x (30.8% faster)
Image applyMask (SIMD off)24.000 ms
Image applyMask (SIMD on)20.000 ms
Image applyMask ratio (SIMD on/off)0.833x (16.7% faster)
Image modifyAlpha (SIMD off)17.000 ms
Image modifyAlpha (SIMD on)12.000 ms
Image modifyAlpha ratio (SIMD on/off)0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off)21.000 ms
Image modifyAlpha removeColor (SIMD on)13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.619x (38.1% faster)

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

Three separate gates rejected generated output. Each is fixed in the generator
so the class of problem cannot come back through a catalog edit.
Unmappable characters. The prose is imported from the developer guide, which
uses typographic punctuation, and `CodenameOne/src` is also compiled by an Ant
javac step with ASCII encoding where a single em dash is
`error: unmappable character for encoding ASCII` -- a build failure, not a
warning. A Unicode escape would not have helped: javac expands `\uXXXX` before
it strips comments, so the character reappears. `toAscii` now folds the
punctuation that actually occurs, and *refuses* anything it has no mapping for
rather than dropping it, because silently deleting a character from a hint's
documentation is the worse outcome.
Broken table. `ios.spm.packages` is documented as `identity|url|requirement`,
and a bare `|` starts a new AsciiDoc cell, so asciidoctor reported "dropping
cells from incomplete row" for the whole 529-row table. Cells are escaped now.
Vale. The guide enforces the Microsoft style as errors, and the generated table
feeds it, so the catalog's prose has to satisfy it too: contractions, no
"and so on", no stray adverbs. A default value is not prose, though -- the one
remaining hit was `android.file_paths`, whose default is an XML fragment -- so
a quoted default now carries the `// vale-skip:` comment .vale.ini documents
for individual false positives.
Also fixes a data bug the guide exposed. The miner preserved Java escape
sequences instead of decoding them, so `android.file_paths` and
`android.facebook_permissions` recorded defaults containing literal
backslashes that the build never sees, and those reached the rendered table.
The miner decodes escapes and re-quotes safely, and the two catalog entries are
corrected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment threadscripts/build_hint_miner.py Fixed

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:96bff9038a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

`killedThreadReportsItselfFinished` failed the Java 21 leg with
"FormTest timed out after 5000ms; edt=initialized pendingSerialCalls=0". The
waits in this class used a 5000ms deadline, which is exactly the `@FormTest`
timeout in EDTTestInterceptor -- so on a loaded runner the poll loop consumed
the entire harness budget and the interceptor fired first. The report then said
only that the method timed out, with nothing about which condition never became
true.
The waits now use 2000ms, well inside the harness budget and still roughly two
thousand times the ~1ms these threads actually take to stop. A genuine
regression now fails on the test's own assertion, which names what went wrong.
Pre-existing (the test arrived with #5526) and unrelated to the build hint work:
core-unittests has no dependency on the JavaSE port, so none of the simulator
registration in this branch runs there, this branch changes nothing under
com.codename1.db or EasyThread, and the Java 8 leg passed the same commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:a343fe3335

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

A mojo's defaultPhase does not bind it to a project -- the project's POM has to
-- and nothing turns a build hint annotation back into a codename1.arg.* pair
except the process-annotations goal. So migrating a project without that
binding deleted working properties and replaced them with annotations no goal
ever reads: the hints vanished from the build with no diagnostic anywhere.
Five projects in this branch were already in that state. gamebuilder,
docs/demos, video-builder and cn1playground bind the plugin but not that goal,
so the binding is added. input-validation-app's common module has no build
section at all, so its migration is reverted rather than inventing a lifecycle
for a demo app.
The goal now checks the reactor for the binding and refuses with the execution
block to paste, so this cannot happen to anyone else.
Three more from the same review:
- The deletion pass recognized only `key=value`. `Properties.load` also accepts
`key:value`, `key value`, escaped separators inside the key, and logical
continuation lines; a declaration it failed to match was left behind while the
annotation was added, so the next build failed with the duplicate-hint error
this goal exists to prevent. Keys are parsed the way Properties.load defines
them now, with a unit test per form.
- The settings file was read as ISO-8859-1 and written back as UTF-8, turning
any unrelated non-ASCII byte -- an accented displayName, say -- into mojibake.
It is written back as ISO-8859-1.
- cn1.androidTheme and cn1.nativeTheme are deprecated aliases of and.themeMode
and nativeTheme, which the builders honour as fallbacks. Neither declared
aliasOf, so conflict detection missed them and one value silently won.
Also: the generation script rebuilt the generator only when its class was
absent, so editing a catalog source and rerunning regenerated every view from
the previous build's bytecode -- reporting success while ignoring the edit, and
passing --check on a tree that was genuinely stale. It always rebuilds now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:0edef42ca4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…plicating a hint
docs/demos is the developer guide's snippet project: deliberately incomplete
code fragments that illustrate @entity, @route, @AppIntent and @mapped. Binding
process-annotations there put those snippets in front of the other processors,
which correctly rejected six of them, so the migration is reverted and its two
hints are back in the properties file. That the project omitted the goal was the
point, not an oversight.
The other three newly bound projects were checked rather than assumed:
gamebuilder, video-builder and cn1playground each run process-annotations
cleanly and emit 6, 3 and 5 hints respectively.
Settings could still create the duplicate the migration is careful to avoid. In
a generated project ios.themeMode and its neighbours are annotations, but the
Build Hints UI decides a hint is inactive from the properties file alone and its
Add button writes a property -- producing a second declaration that fails the
next build. The tool now reads META-INF/codenameone/build-hints.properties, the
file the processor writes on every build and deletes when the last annotation
goes, and renders those hints read-only with the attribute that owns them:
"Set by @iOS(themeMode) on the main class." An unbuilt project has no such file
and behaves as before.
Also fixes the SpotBugs finding this branch introduced: `backslashes % 2 == 1`
in the continuation scan is false for negative odd numbers, so it is `!= 0`.
The count cannot go negative, but the idiom is wrong regardless of that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Keep Initializr templates compatible with the pinned runtime

Every Initializr source archive now imports com.codename1.annotations.buildhints and uses the new annotations, while GeneratorModel.CN1_PLUGIN_VERSION still rewrites generated projects to 7.0.267, whose codenameone-core predates that package; the generated common POM also omits process-annotations. Consequently all newly downloaded Initializr projects fail compilation instead of receiving the defaults removed from common.zip's settings file. Leave these templates property-backed until Initializr targets the release containing this feature, or bump the generated version and bind the processor.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 22, 2026

Copy link
Copy Markdown
CollaboratorAuthor

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 339 seconds

Detailed Performance Metrics

MetricDuration
SIMD kernel backendSSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300)java 62ms / native 3ms = 20.6x speedup
SIMD float-mul (64K x300)java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctnessPASS (native result == scalar reference)
Base64 payload size8192 bytes
Base64 benchmark iterations6000
Base64 SIMD byte pathactive (NEON-accelerated)
Base64 CN1 encode167.000 ms
Base64 CN1 decode120.000 ms
Base64 native encode624.000 ms
Base64 encode ratio (CN1/native)0.268x (73.2% faster)
Base64 native decode231.000 ms
Base64 decode ratio (CN1/native)0.519x (48.1% faster)
Base64 SIMD encode56.000 ms
Base64 encode ratio (SIMD/CN1)0.335x (66.5% faster)
Base64 SIMD decode45.000 ms
Base64 decode ratio (SIMD/CN1)0.375x (62.5% faster)
Base64 encode ratio (SIMD/native)0.090x (91.0% faster)
Base64 decode ratio (SIMD/native)0.195x (80.5% faster)
Image encode benchmark iterations100
Image createMask (SIMD off)6.000 ms
Image createMask (SIMD on)2.000 ms
Image createMask ratio (SIMD on/off)0.333x (66.7% faster)
Image applyMask (SIMD off)46.000 ms
Image applyMask (SIMD on)36.000 ms
Image applyMask ratio (SIMD on/off)0.783x (21.7% faster)
Image modifyAlpha (SIMD off)39.000 ms
Image modifyAlpha (SIMD on)39.000 ms
Image modifyAlpha ratio (SIMD on/off)1.000x (0.0% slower)
Image modifyAlpha removeColor (SIMD off)47.000 ms
Image modifyAlpha removeColor (SIMD on)32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off)0.681x (31.9% faster)

Every project the archetype and the initializr produce is pinned to a released
Codename One version -- the initializr hardcodes 7.0.267 in
GeneratorModel.CN1_PLUGIN_VERSION -- and no released core carries
com.codename1.annotations.buildhints. So a generated project would import
annotations that do not resolve and fail to compile before the user has written
a line, and the settings those templates stopped declaring would simply be gone.
The templates are reverted to exactly their previous state: the archetype's
__mainName__.java and codenameone_settings.properties, and the initializr's
common.zip and four source archives. They can move to annotations in a follow-up
once a release containing the package is out.
The generated build hint table is dropped from the agent skill reference for the
same reason -- it documented a form those projects cannot use yet -- so the
generator no longer rewrites markdown at all.
What stays from that area is unrelated to annotations: the skill reference
described build hints that no builder reads, so a reader copying them got a
green build and no effect. android.xPermissions is spelled android.xpermissions,
android.minSdkVersion is android.min_sdk_version, and android.sdkVersion,
android.googlePlayVersion, build.compile, build.timeout, javascript.html5,
javascript.bundleResources and ios.orientation do not exist at all. Those
corrections are right for the published version too, and the catalog gate now
holds our own documentation to them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
compilesJava() already withheld the conventional src/main/java from the search
when the POM chain switches default-compile off with <phase>none</phase> and
binds nothing in its place. <sourceDirectory> is the same root, declared instead
of assumed, and it was not gated -- so a module that compiles nothing with javac
still offered it, and a stale copy of the main class there answered ahead of the
compiled source.
Per element rather than over the whole list, because a Kotlin-only module is
exactly the case where javac does not run and its <sourceDirs> must still be
searched. Both directions are asserted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:705783d9b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/release-on-maven-central.yml
shai-almogand others added 10 commits August 27, 2026 11:08
…writes
The .java branch in write() guarded against a non-ASCII character reaching a
generated Java source; the generator writes a JSON data file and an asciidoc
table and nothing else. toAscii survives because the reader test still needs it
-- the catalog's prose came from the developer guide and the annotations are
ASCII Java sources, so the two cannot be compared verbatim -- and its
documentation now says that rather than describing a build step it no longer
feeds.
Also the HintGroup local in Bindings that nothing read, and the no-op continue
at the end of the loop that was its only use. toHint already resolves the group
and throws when an annotation type names none.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Central recovery poll queried the two build hint artifacts with a single
curl carrying two URLs. curl needs an -o per URL and emits --write-out after
each transfer, so the second POM body arrived on stdout with both status codes
appended: the captured value could never equal 200, and the poll -- which exists
to rescue a release Central has already accepted -- would exhaust all 90
attempts and fail it. Reported by codex.
Both this poll and the R2 confirmation walk a list of artifacts, and each
carried its own copy, which had already drifted: Central's omitted
codenameone-core. The list is now named once in the job environment and both
read it, with a guard that refuses an empty list rather than reporting success
for having checked nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aliasOf, deprecated, external, enterpriseOnly and link exist so an annotated hint
can say what the catalog entries already say, and none of the 87 shipped
attributes needs any of them -- so the reader's handling of all five was carried
by no test at all. Exercised now against a copy of the real annotation package
with one probe attribute added, which is what the reader compiles anyway.
Kept rather than deleted: the first annotated hint that has to be marked
deprecated needs somewhere to say so, and would otherwise be the first thing ever
to run that path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
validateScreenshotContent threw before the write, so a failing scenario left
nothing behind but a pixel count -- which is exactly what happened to the
component-inspector run that reported textPixels=738 with no image to look at.
The capture is now written as <name>.png.rejected.png first, into the directory
the workflow already uploads with if: always(), so the next occurrence can be
diagnosed instead of guessed at.
Not a fix for that failure: the check reads a fixed screen rectangle and the run
before and after it were green on the same commit, so what it saw is still
unknown. This is what makes finding out possible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e</phase>
compilesJava() answered from the first level in the chain that switched
default-compile off, so an ancestor that disables it decided for a child that
binds its own compile execution. A module that plainly does compile Java read as
one that does not, and its Java roots were then withheld from the search -- where
a Kotlin or stale copy of the main class answers for it instead.
An enabled compile binding anywhere in the chain now settles it, and it is looked
for before any disabling level is allowed to decide. Extracted to a static form
taking the chain directly, because the walk needed a project on disk and was the
untested half of a method whose two halves are separately covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
attributeOf was written to replace a substring search for combine.self, and then
looked for the attribute name anywhere inside the start tag -- so xcombine.self
would have answered for it. The same rule, reproduced one level down in the fix
for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…were masking
Two failures on the JDK 8 leg, one the cause of the other.
The module's POM binds cn1:css at process-classes and the CSS compiler opens a
JFrame, which in this container is java.awt.HeadlessException before a single
test runs. Run under xvfb-run, which this job already uses for the Ant build a
few steps later.
That failure then skipped every step after it, including "Run SpotBugs for
ByteCodeTranslator" -- so the quality report failed for a missing
ByteCodeTranslator report, which said nothing about what had actually broken.
The step now runs last among this leg's gates, which also keeps its reinstall of
the maven plugin away from the SpotBugs reports the earlier steps produce.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three groups, none of which any reviewer asked for and all of which I added off
my own red-team pass:
The Settings POM-parser rewrites -- depth-matched </plugin>, combine.self and
combine.children read off their own element, comment stripping in parentPomPath,
the compilesJava chain walk, and the <sourceDirectory> gate. These change how the
Settings tool parses arbitrary user POMs, on the strength of cases nobody
reported. The parser is back to what it was.
The simulator verifier's rejected-capture diagnostics, which have nothing to do
with build hints -- I added them while chasing a screenshot failure that predates
this branch.
The release workflow's shared RELEASE_ARTIFACTS list. Only the defect codex
reported is kept: the two build hint POMs are queried with one curl each, because
two URLs share the single -o and the captured value could never be 200.
The plugin-side cleanups that rode along in the same commit as the parser work --
the unused ProcessorContext constructors and LibraryHintMerger's duplicate
prefix -- are kept, as is the comment de-duplication.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A generated file in git conflicts on every merge and there is nothing for a hand
edit to survive in, so it should never have been checked in.
It cannot simply move to one module's target/classes: BuildHints.entries() loads
it, so every consumer of the catalog needs it on its own classpath, and the
generator lives in build-hint-tools which depends on the catalog -- the catalog
cannot render its own resource without a dependency cycle. So each of the three
modules that actually needs it renders it into its own target/classes at
process-classes: maven/javase, maven/codenameone-maven-plugin and
scripts/settings/common. exec:java with classpathScope=compile, because a
plugin-level dependency resolves from the repository rather than the reactor and
would be a chicken and egg on a clean checkout. build-hint-tools is provided
scope in the two application modules so ASM cannot reach a built app.
Verified: all three render byte-identically to the file that was committed, the
catalog jar no longer carries it, and the javase jar does.
Consequences elsewhere. gen-build-hint-annotations.sh renders to a scratch
directory, and --check no longer diffs against a committed copy -- there is none
-- but asserts the rendering still succeeds and produces the full set, which is
the failure that would otherwise reach a user as an editor with no hints in it.
check-build-hint-catalog.py read the committed path and returned an empty set
when it was absent, which failed the gate OPEN; it now reads whichever module has
rendered it and says to build one when none has. The Ant JavaSE build takes it
from maven/javase/target/classes when that build has run, and without it the
simulator keeps the hints BuildHintSchemaDefaults compiles in and says the
annotated ones are missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Removing the committed data file broke two catalog tests, and I did not see it
because I verified that module with -DskipTests -- on the one module whose
structure I had just changed.
BuildHints.entries() is complete only on a classpath carrying the rendered data
file. The catalog module cannot render one for its own tests: the generator lives
in build-hint-tools, which depends on the catalog. So the two assertions that
need the complete set -- every alias resolves to a real hint, and the catalog
agrees with LibraryHintMerger on every separator it defines -- move to
build-hint-tools, which renders the data into its own test classes first. Both
fail without that render, which is checked.
What stays beside the catalog is what its own sources declare, and the class now
says so rather than looking like it covers everything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:9b07db8e93

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread.github/workflows/developer-guide-docs.yml
shai-almogand others added 3 commits August 27, 2026 14:05
…lows
Making the table generated instead of committed took away the trigger it used to
get for free: while it was checked in, editing it showed up as a docs diff. The
replacement path list named the catalog and the render script but not the two
inputs that matter most -- the annotations, whose javadoc is the Description
column verbatim, and build-hint-tools, which is the renderer itself. A PR
renaming an attribute or rewording its documentation could therefore change the
guide without ever running the AsciiDoc and Vale checks over the result.
Reported by codex.
Fixed in both copies of that list in developer-guide-docs.yml, since triggering
the workflow is not enough on its own -- the HTML and PDF build is gated on the
paths-filter as well, and the two had already drifted.
And in website-docs.yml, which is the other workflow that renders the same table
and had the same gap for the same reason. Its pull_request and push lists both
already covered CodenameOne/src/**, so only the catalog, the renderer and the
script were missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two files overlapped and both auto-merged: website-docs.yml keeps master's
website telemetry step alongside the build hint table paths added here, and
CLAUDE.md keeps master's restructure -- 163 lines cut and the GC notes moved to
vm/CLAUDE.md -- alongside this branch's build hints section.
That section is rewritten rather than merged as-is: it still described writing
cn1-build-hints.json into the tree and told the reader to run the generator to
"rewrite the data file". Nothing is written into the tree any more, so it now
says where the file is rendered instead, and the command list loses the step that
no longer does anything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
windows-tooling builds the Settings tool with -Dcodename1.platform=javase, which
activates a profile declaring exec-maven-plugin with its own plugin level
<arguments> containing a <classpath/> element. Maven merges plugin level
configuration into every execution of that plugin, so that Object landed in the
generator execution's String array:
Cannot store value into array: ... can not cast one of the elements of
java.lang.Object[] to the type of the destination array, java.lang.String
A plain local build never activates that profile, which is why four green local
runs said nothing. Reproduced with the property set, fixed with
combine.self="override" on the arguments, and re-verified against the exact
command the workflow runs. Applied to all four generator executions rather than
only the one that failed: the collision needs a second declaration of the same
plugin anywhere in the effective build, which is not a property of this module.
Also restores the simulator verifier's rejected capture. It was reverted as out
of scope, and then the component inspector scenario failed a second time with a
byte identical textPixels=738 -- twice the same number is a state, not a race --
with no image kept to say what was on screen. Two failures in 22 runs on this
branch and none in 38 on any other is too specific to guess at.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:2d2708a0c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almogand others added 5 commits August 27, 2026 14:50
BytecodeComplianceMojo rewrites class files in place -- capping a class to the
supported version, and redirecting a call the runtime does not have. The build
hint manifest records the main class's own bytes, because the simulator has no
bytecode reader and can only compare the class file itself. If the main class is
one of the rewritten ones, a manifest written before that goal describes a class
that no longer exists on disk, and the simulator reads a manifest generated
moments earlier as stale and publishes none of the annotated hints. Reported by
codex.
Every pom in this repository happens to run process-annotations after this goal,
where the stamp is taken from the rewritten bytes anyway, so nothing is broken
today -- I checked all twelve. Nothing enforces that order though, and the
failure mode is silent: hints disappear, no error. Re-stamping here as well makes
the order stop mattering, since whichever of the two runs last leaves a manifest
describing the class that is actually there. It is a no-op when there is no
manifest, which is every project that declares its hints in the properties file.
The test asserts the hazard and the repair together: capping a version changes
the class, which invalidates a stamp taken before it, and re-stamping restores
the match without touching the hints. Removing the re-stamp fails it. What the
test does not cover is the one-line call from executeImpl, which needs a
MavenProject to reach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…wants
Both predate this branch without one. check-copyright-headers is scoped to the
PR's diff, so editing them is what brought them into scope -- the gate working as
designed, not a new defect. They take the Codename One GPLv2 + Classpath
Exception header their siblings in this package carry.
I ran that gate locally before the last push and it reported success while
checking nothing: with no --base it has no diff to scope to, and "0 file(s)
passed" was the tell I read straight past. Run with --base origin/master it
checks the same 128 files CI does, and passes. check-cast-semantics.sh takes
--baseline, a ratchet file rather than a git ref, so it is whole-repo and running
it bare is valid; copyright was the only gate here with that shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guide said the compiler-checked form existed and the generated table named
the annotation for each hint, but nothing anywhere showed what one looks like. A
reader had the Annotation column and no way to turn `@Ios(pods)` into code.
Adds a worked example and the exact properties lines it replaces, then explains
the three things about the syntax that are not guessable from the table: a list
hint takes a Java array and the build joins it with that hint's own separator, a
boolean hint takes Toggle rather than boolean so that leaving it out means the
build decides, and a hint with a closed value set takes an enum. Also says
plainly that the long tail and the open-ended families stay in the properties
file and that the two forms mix freely.
Every mapping in the example is checked against the generated data rather than
written from memory: each attribute resolves to the hint name claimed, each enum
constant to the wire value claimed, and ios.pods really does join with a comma.
The Java snippet compiles against the annotations, the guide's snippet validator
passes (inline blocks are refused there, so both snippets live in docs/demos),
Vale is clean, and the chapter renders without warnings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LanguageTool fails the developer guide build on a single match, and I had
hyphenated it in one sentence and not the other. Caught by running the gate
locally on the rendered chapter rather than by a CI cycle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A new .java file, so the diff-scoped gate checks it. I ran that gate with a base
earlier and it passed, then added this file and pushed without re-running it --
which is exactly what my own note about running diff-scoped gates AFTER the
commit is for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:6fd1406078

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadPorts/JavaSE/src/com/codename1/impl/javase/Simulator.java
A project that adds @build or @DesktopBuild to its main class but never binds
cn1:process-annotations -- an upgraded or hand-written POM -- compiles and
launches fine, emits no manifest, and the simulator returned quietly and applied
none of the annotated hints. CN1BuildMojo refuses a device build for exactly this
case, so the simulator was the one place where the hints vanished with nothing
said and local behaviour diverged from device behaviour for the same project.
Reported by codex.
It now says so. A warning rather than a refusal: the simulator's job is to start,
and what it is missing are build settings, not something it cannot run without.
Detected by scanning the main class file's bytes for the annotations package,
because the simulator has no bytecode reader and an annotation's type is in the
constant pool as a descriptor. That is wider than reading the annotation table --
a main class that merely mentions the package matches too -- which is why this
warns and does not refuse.
Three cases are asserted: annotated with no manifest warns and names both the
class and the goal, an unannotated main class is silent, and a main class absent
from the classpath is silent. The first fails if the marker check is removed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:b0f5213ef4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import com.codename1.annotations.buildhints.Toggle;

// tag::buildHintAnnotations[]
@Android(themeMode = ThemeMode.MODERN, minSdkVersion = AndroidMinSdk.API_24)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the documentation snippet out of application bytecode

When docs/demos is built for a device through its reactor, this source is compiled into the common module even though codenameone_settings.properties names com.codenameone.developerguide.DemoCode as the main class. CN1BuildMojo.failOnMisplacedAnnotations() rejects live build-hint annotations on every non-main class, so BuildHintAnnotationSnippet makes that build fail before submission; binding process-annotations would instead fail during process-classes. Store the include outside the compiled source root or render it from a noncompiled snippet.

Useful? React with 👍 / 👎.

The developer guide's quality gate counts Vale alerts at suggestion level and
fails on any of them. I checked the chapter locally with --minAlertLevel=error,
which hides warnings, so two Microsoft.Adverbs warnings -- "deliberately" and
"freely" -- went through and reddened the build. Both sentences say the same
thing without the adverb.
Re-checked at the level CI uses: Vale 0/0/0, LanguageTool 0 matches, snippet
validator 706 blocks, paragraph capitalization clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shai-almog
shai-almog merged commit d21a389 into masterAug 27, 2026
72 checks passed
@shai-almog
shai-almog deleted the build-hint-annotations branch August 27, 2026 17:13
shai-almog added a commit that referenced this pull request Aug 27, 2026
Two conflicts. .gitignore had a new entry on each side and keeps both.
Advanced-Topics-Under-The-Hood.asciidoc is the one that mattered: master's #5586
replaced the hand-written build hint table with an include generated from
maven/build-hint-catalog, while this branch had added 28 macos.* rows to that
table. Resolving to master's include alone would have compiled cleanly and
silently deleted the documentation for every macOS build hint the port added --
the table is generated now, so a hint absent from the catalog has no
documentation anywhere.
So the 28 hints move into BuildHintsApple with the descriptions they had in the
table, and the generated table carries all 28 again. Confirmed by running
scripts/gen-build-hint-table.sh and counting them in the output, not by reading
the diff.
macNative.iosMinDeploymentTarget was already in the catalog and is NOT
duplicated; the first pass nearly added it back because a name pattern stopped
at the underscore in macos.add_libs and mis-parsed that row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Aug 27, 2026
master's #5586 made annotations the source for build hints that have one, and
the merge landed this port's 28 macos.* hints as hand-written catalog entries --
in HintGroup.MAC_NATIVE, whose key prefix is macNative. and which carries no
annotation at all. They belonged in the feature, not beside it.
HintGroup gains MAC_OS("Mac", "macos."), and @Mac declares all 28 as
compile-checked attributes: Toggle for the nine booleans, an appendable String[]
for addLibs following the ios.add_libs shape, and an explicit name for the
fifteen whose tail has dots and cannot be derived from a method name. The
hand-written entries are gone, because two sources for one hint is the drift
this feature exists to remove. macNative.* stays hand written: it is the
spelling the legacy Catalyst target reads and no annotation offers it.
Verified by regenerating rather than by reading the diff -- 28 macos rows in the
guide table, macos.entitlements.appSandbox typed boolean, and the catalog and
tools tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@shai-almog