Standard jakarta.validation annotations, compiled once per type into a flat
check table. No configuration: constraints come from the annotations already on
your types, and ValidationException extends HttpException with status 422 so
the default handler renders it without this extension registering anything.
record CreateUser(@NotBlank @Size(max = 80) String name,
@Email String email,
@Min(18) int age) {}
CreateUser dto = validation.body(req, CreateUser.class);
Annotations only — Hibernate Validator's engine is deliberately absent. It
resolves constraints reflectively per call and pulls ~2 MB plus EL, which is
the per-request cost this module exists to avoid. jakarta.validation-api is
~90 KB of annotations.
The passing path allocates nothing. Constraints resolve at first use into an
opcode plus operands cached in a ClassValue, so there is no map lookup and no
lock. Fields are read through MethodHandles adapted to an exact signature —
(Object)Object for references, (Object)long for primitive integrals — so
invokeExact neither boxes nor builds the argument array Field.get and
Method.invoke allocate. Checks are a flat array walked by a tableswitch rather
than a class hierarchy behind a virtual call. @Size reads a length the object
already knows and @Email scans with indexOf, because Pattern.matcher allocates
a matcher and two int arrays per call. Messages are pre-rendered at compile
time. The violation list and the exception exist only once something fails.
@Pattern is the marked exception: its regex compiles once but matcher()
allocates per call.
Constraints are read from declared fields, so records and plain classes take
one code path — a constraint on a record component propagates to its backing
field.
Jakarta null semantics are exact: only @NotNull rejects null.
flash-ext-openapi now mirrors the same annotations into the generated schema —
minLength, maxLength, minItems, minimum, maximum, pattern, format: email and
required — via an optional jakarta.validation dependency detected at boot. A
type declares its rules once and both the validator and the published contract
read them. An explicit @Schema still wins; the bridge only fills keys nobody
set, and without the annotations on the classpath the bridge class is never
loaded.
flash-ext-jackson is optional too: validate(value) works without it, only
body(req, type) needs a codec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4.7 KiB
4.7 KiB
Flash — Agent Guidelines
This document defines the conventions and rules that all agents (AI or human) must follow when working on this repository. Read it entirely before making any change.
Git Workflow
Branch Naming
| Type | Pattern | Example |
|---|---|---|
| New feature | feature/<scope>/<short-description> |
feature/ext-oidc/pkce-support |
| Bug fix | fix/<scope>/<short-description> |
fix/core/router-npe |
| Hotfix on released version | hotfix/<version>/<short-description> |
hotfix/2.0.1/auth-bypass |
| CI/infra changes | feature/ci/<short-description> |
feature/ci/add-snapshot-workflow |
Rules:
- Always branch from
master. - Branch names are lowercase, words separated by
-. - Never push directly to
master. - Never create
develop,release/*, or any other long-lived branch.
Commit Messages — Conventional Commits
Format: <type>(<scope>): <short description>
| Type | When to use |
|---|---|
feat |
New feature |
fix |
Bug fix |
refactor |
Code change without feature/fix |
test |
Adding or updating tests |
docs |
Documentation only |
chore |
Build, deps, tooling — no production code |
ci |
Changes to GitHub Actions workflows |
Allowed scopes: core, testing, ext-jackson, ext-openapi, ext-oidc, ext-routeviewer,
ext-view-core, ext-view-jte, ext-view-thymeleaf, ext-limiter, ext-web-bundler,
ext-mcp, ext-validation, ext-data-core, ext-data-jdbc, ext-data-hibernate, release, deps, ci.
Examples:
feat(ext-oidc): add PKCE support
fix(core): fix NPE in RouteHandler when path is null
chore(deps): upgrade jackson to 2.18.0
ci: add timeout to snapshot workflow
chore(release): 2.1.0
Pull Requests
- Every branch must be merged via PR, never with a direct push.
- PR title must follow Conventional Commits format.
- CI (
ci.yml) must be green before merging. - Squash merge is preferred for
feature/*andfix/*to keep history clean. - Merge commit is preferred for hotfixes (preserves the fix commit intact).
Versioning
- All modules share a single version defined in the root
pom.xml(flash-parent). - Never change the version in child POMs — always and only in the parent.
- Current scheme:
MAJOR.MINOR.PATCH- MAJOR: breaking API changes
- MINOR: new backward-compatible features
- PATCH: backward-compatible bug fixes on an already-released version
- During development, master always carries a
-SNAPSHOTversion. - Never manually edit the version — versions are bumped exclusively by the
prepare-releaseGitHub Actions workflow.
Release Process (for maintainers only)
- Ensure
masteris green (CI passing). - Go to GitHub Actions →
Prepare Release→Run workflow. - Input
version(e.g.2.1.0) andnext_version(e.g.2.2.0). - The workflow handles everything: bump, commit, tag, push.
- The
releaseworkflow then triggers automatically on the tag.
Maven & Module Structure
- Root POM:
flash-parent— defines all dependency versions and plugin config. flashmodule: the core framework JAR.flash-testingmodule: JUnit 5 harness for testing Flash applications. Deliberately not underflash-extensions/— it is not something youinstall(), and it carriesjunit-jupiter-apiat compile scope.flash-extensionsPOM: aggregator for all extension modules.- Extensions live under
flash-extensions/flash-ext-*/. - When adding a new extension:
- Add the module to
flash-extensions/pom.xml<modules>. - Add the dependency to
flash-extensions/pom.xml<dependencyManagement>. - Add the dependency to the root
pom.xml<dependencyManagement>. - Do not declare a
<version>in the new module's POM — it inherits from the parent.
- Add the module to
CI/CD Pipelines
| Workflow | Trigger | What it does |
|---|---|---|
ci.yml |
Push to any branch, PR to master | Compile + test — required gate |
snapshot.yml |
Push to master |
Deploy -SNAPSHOT to maven.relism.dev/snapshots |
prepare-release.yml |
Manual workflow_dispatch |
Bump version, commit, tag, push |
release.yml |
Push of tag v* |
GPG sign, deploy to /releases, JavaDoc to GitHub Pages, GitHub Release |
Agents must never manually trigger prepare-release or modify version strings.
What Agents Must NOT Do
- Push directly to
masterorgh-pages. - Manually edit
<version>tags in any POM. - Add new
<repositories>or<distributionManagement>entries without explicit instruction. - Modify
.github/workflows/*.ymlfiles without explicit instruction. - Commit generated files (
target/,*.class,*.versionsBackup). - Use
git push --forceon any branch.