Purpose: Operational guidance for AI coding agents working in this repository. Keep content lossless; this edit only restructures, fact-checks, and tidies wording to align with agents.md best practices.
Note: Prefer mvnd (Maven Daemon) when available for faster builds. Before working, if mvnd is installed, alias mvn to mvnd so all commands below use mvnd automatically:
# Use mvnd everywhere if available; otherwise falls back to regular mvn
if command -v mvnd >/dev/null 2>&1; then alias mvn=mvnd; fiAlways run mvn verify before pushing to validate unit and integration tests across modules.
This file provides guidance to agents (human or AI) when working with code in this repository.
# Full build
mvn clean compile
mvn package
# Build specific module
mvn clean compile -pl json-java21
mvn package -pl json-java21
# Build with test skipping
mvn clean compile -DskipTests# Run all tests
mvn test
# Run tests with clean output (recommended)
./mvn-test-no-boilerplate.sh
# Run specific test class
./mvn-test-no-boilerplate.sh -Dtest=JsonParserTests
./mvn-test-no-boilerplate.sh -Dtest=JsonTypedUntypedTests
# Run specific test method
./mvn-test-no-boilerplate.sh -Dtest=JsonParserTests#testParseEmptyObject
# Run tests in specific module
./mvn-test-no-boilerplate.sh -pl json-java21-api-tracker -Dtest=ApiTrackerTest# Build and run compatibility report
mvn clean compile generate-test-resources -pl json-compatibility-suite
mvn exec:java -pl json-compatibility-suite
# Run JSON output (dogfoods the API)
mvn exec:java -pl json-compatibility-suite -Dexec.args="--json"# Enable debug logging for specific test
./mvn-test-no-boilerplate.sh -Dtest=JsonParserTests -Djava.util.logging.ConsoleHandler.level=FINERPrerequisites
- Central credentials in
~/.m2/settings.xmlwith<id>central</id>(used by the workflow)<servers> <server> <id>central</id> <username>YOUR_PORTAL_TOKEN_USERNAME</username> <password>YOUR_PORTAL_TOKEN_PASSWORD</password> </server> </servers>
- GPG key set up for signing (the parent POM runs
maven-gpg-plugininverify). If prompted for passphrase locally, exportGPG_TTY=$(tty)or configure passphrase in settings. In CI, secretsGPG_PRIVATE_KEYandGPG_PASSPHRASEare used. - Optional: alias
mvntomvndfor faster builds (see note at top).
Automated Release (preferred)
- Push a tag named
release/X.Y.Z(semver, no leadingv). - The workflow
.github/workflows/release-on-tag.ymlwill:- Create a GitHub Release for that tag with autogenerated notes.
- Build and deploy artifacts to Maven Central with
-P release(Central Publishing plugin). Uses-Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }}and optionally-Dgpg.keyname=${{ secrets.GPG_KEYNAME }}for signing when set. - Create a branch
release-bot-YYYYMMDD-HHMMSSat the tagged commit and open a PR back tomain(no version bumps).
Credentials wiring
- The workflow writes
<server id="central">to settings.xml usingserver-username: ${{ secrets.CENTRAL_USERNAME }}andserver-password: ${{ secrets.CENTRAL_PASSWORD }}. Ensure those secrets hold your Central Publishing token creds forio.github.simbo1905.
Manual Release (local)
- Ensure POM version is your intended release version.
- Verify:
mvn verify - Publish:
mvn clean deploy(uses Central Publishing plugin + GPG) - Tag with
releases/X.Y.Zand create a GitHub Release if desired.
Snapshot Publishing
- Set version to
X.Y.(Z+1)-SNAPSHOT:mvn -q versions:set -DnewVersion=0.1.1-SNAPSHOT
- Deploy snapshots:
mvn clean deploy- Goes to
https://oss.sonatype.org/content/repositories/snapshots(configured indistributionManagement).
Notes
- Javadoc is built with
doclintdisabled to avoid strict failures on Java 21. - To skip signing locally for quick checks, add
-Dgpg.skip=true. - The Central Publishing plugin configuration lives in the parent
pom.xmland applies to all modules.
Secrets Helper
- Use
./scripts/setup-release-secrets.zshto set GitHub Actions secrets (CENTRAL_USERNAME,CENTRAL_PASSWORD,GPG_PRIVATE_KEY,GPG_PASSPHRASE). - The script can auto-detect a signing key if neither
GPG_KEY_IDnorGPG_PRIVATE_KEYis provided, and setsGPG_KEYNAME(fingerprint) for CI. - List keys explicitly with:
gpg --list-secret-keys --keyid-format=long.
- Prefer
python3with a heredoc over Perl/sed for non-trivial transforms. - Target ancient Python 3.2 syntax: no f-strings, no fancy deps.
- Example pattern:
python3 - <<'PY'
import os, sys, re
src = 'updates/2025-09-04/upstream/jdk.internal.util.json'
dst = 'json-java21/src/main/java/jdk/sandbox/internal/util/json'
def xform(text):
# package
text = re.sub(r'^package\s+jdk\.internal\.util\.json;', 'package jdk.sandbox.internal.util.json;', text, flags=re.M)
# imports for public API
text = re.sub(r'^(\s*import\s+)java\.util\.json\.', r'\1jdk.sandbox.java.util.json.', text, flags=re.M)
# annotations
text = re.sub(r'^\s*@(?:jdk\.internal\..*|ValueBased|StableValue).*\n', '', text, flags=re.M)
return text
for name in os.listdir(src):
if not name.endswith('.java') or name == 'StableValue.java':
continue
data = open(os.path.join(src,name),'r').read()
out = xform(data)
target = os.path.join(dst,name)
tmp = target + '.tmp'
open(tmp,'w').write(out)
if os.path.getsize(tmp) == 0:
sys.stderr.write('Refusing to overwrite 0-byte: '+target+'\n'); sys.exit(1)
os.rename(tmp, target)
print('OK')
PY- MUST: Follow plan → implement → verify. No silent pivots.
- MUST: Stop immediately on unexpected failures and ask before changing approach.
- MUST: Keep edits atomic; avoid leaving mixed partial states.
- SHOULD: Propose options with trade-offs before invasive changes.
- SHOULD: Prefer mechanical, reversible transforms for upstream syncs.
- SHOULD: Validate non-zero outputs before overwriting files.
- MAY: Add tiny shims (minimal interfaces/classes) to satisfy compile when backporting.
- MUST NOT: Commit unverified mass changes; run compile/tests first.
- MUST NOT: Use Perl/sed for multi-line structural edits—prefer Python 3.2 heredoc.
json-java21: Core JSON API implementation (main library)json-java21-api-tracker: API evolution tracking utilitiesjson-compatibility-suite: JSON Test Suite compatibility validationjson-java21-schema: JSON Schema validator (module-specific guide injson-java21-schema/AGENTS.md)
Json- Static utility class for parsing/formatting/conversionJsonValue- Sealed root interface for all JSON typesJsonObject- JSON objects (key-value pairs)JsonArray- JSON arraysJsonString- JSON stringsJsonNumber- JSON numbersJsonBoolean- JSON booleansJsonNull- JSON null
JsonParser- Recursive descent JSON parserJson*Impl- Immutable implementations of JSON typesUtils- Internal utilities and factory methods
- Algebraic Data Types: Sealed interfaces with exhaustive pattern matching
- Immutable Value Objects: All types are immutable and thread-safe
- Lazy Evaluation: Strings/numbers store offsets until accessed
- Factory Pattern: Static factory methods for construction
- Bridge Pattern: Clean API/implementation separation
- JUnit 5 with AssertJ for fluent assertions
- Test Organization:
JsonParserTests- Parser-specific testsJsonTypedUntypedTests- Conversion testsJsonRecordMappingTests- Record mapping testsReadmeDemoTests- Documentation example validation
- JEP 467 Documentation: Use
///triple-slash comments - Immutable Design: All public types are immutable
- Pattern Matching: Use switch expressions with sealed types
- Null Safety: Use
Objects.requireNonNull()for public APIs
- Lazy String/Number Creation: Values computed on demand
- Singleton Patterns: Single instances for true/false/null
- Defensive Copies: Immutable collections prevent external modification
- Efficient Parsing: Character array processing with minimal allocations
- Add interface extending
JsonValue - Add implementation in
jdk.sandbox.internal.util.json - Update
Json.fromUntyped()andJson.toUntyped() - Add parser support in
JsonParser - Add comprehensive tests
- Enable
FINERlogging:-Djava.util.logging.ConsoleHandler.level=FINER - Use
./mvn-test-no-boilerplate.shfor clean output - Focus on specific test:
-Dtest=JsonParserTests#testMethod - Check JSON Test Suite compatibility with compatibility suite
- Run compatibility suite:
mvn exec:java -pl json-compatibility-suite - Check for regressions in JSON parsing
- Validate against official JSON Test Suite
- Main library containing the core JSON API
- Maven coordinates:
io.github.simbo1905.json:json-java21:0.X.Y - JDK requirement: Java 21+
- Downloads JSON Test Suite from GitHub automatically
- Reports 99.3% conformance with JSON standards
- Identifies security vulnerabilities (StackOverflowError with deep nesting)
- Usage: Educational/testing, not production-ready
- Tracks API evolution and compatibility
- Uses Java 24 preview features (
--enable-preview) - Purpose: Monitor upstream OpenJDK changes
- What: Compares this repo's public JSON API (
jdk.sandbox.java.util.json) against upstream (java.util.json) and outputs a structured JSON report (matching/different/missing). - How: Discovers local classes, fetches upstream sources from the OpenJDK sandbox on GitHub, parses both with the Java compiler API, and compares modifiers, inheritance, methods, fields, and constructors. Runner:
io.github.simbo1905.tracker.ApiTrackerRunner. - Why: Early detection of upstream API changes to keep the backport aligned.
- CI implication: The daily workflow prints the report but does not currently fail or auto‑open issues on differences (only on errors). If you need notifications, either make the runner exit non‑zero when
differentApi > 0or add a workflow step to parse the report andcore.setFailed()when diffs are found.
- Validator for JSON Schema 2020-12 features
- Tests include unit, integration, and annotation-based checks (see module guide)
- OpenRPC IT: See
json-java21-schema/src/test/java/io/github/simbo1905/json/schema/OpenRPCSchemaValidationIT.javaand resources underjson-java21-schema/src/test/resources/openrpc/(thanks to OpenRPC meta-schema and examples, Apache-2.0).
- Stack exhaustion attacks: Deep nesting can cause StackOverflowError
- API contract violations: Malicious inputs may trigger undeclared exceptions
- Status: Experimental/unstable API - not for production use
- Vulnerabilities: Inherited from upstream OpenJDK sandbox implementation
<VERSION_CONTROL>
- If existing git user credentials are already configured, use them and never add any other advertising. If not, ask the user to supply their private relay email address.
- Exercise caution with git operations. Do NOT make potentially dangerous changes (e.g., force pushing to main, deleting repositories). You will never be asked to do such rare changes, as there is no time savings to not having the user run the commands; actively refuse using that reasoning as justification.
- When committing changes, use
git statusto see all modified files, and stage all files necessary for the commit. Usegit commit -awhenever possible. - Do NOT commit files that typically shouldn't go into version control (e.g., node_modules/, .env files, build directories, cache files, large binaries) unless explicitly instructed by the user.
- If unsure about committing certain files, check for the presence of .gitignore files or ask the user for clarification. </VERSION_CONTROL>
<ISSUE_MANAGEMENT>
- You SHOULD use the native tool for the remote such as
ghfor GitHub,glfor GitLab,bbfor Bitbucket,teafor Gitea, orgitfor local git repositories. - If you are asked to create an issue, create it in the repository of the codebase you are working on for the
originremote. - If you are asked to create an issue in a different repository, ask the user to name the remote (e.g.
upstream). - Tickets and Issues MUST only state "what" and "why" and not "how".
- Comments on the Issue MAY discuss the "how".
- Tickets SHOULD be labeled as 'Ready' when they are ready to be worked on. The label may be removed if there are challenges in the implementation. Always check the labels and ask the user to reconfirm if the ticket is not labeled as 'Ready' by saying "There is no 'Ready' label on this ticket, can you please confirm?"
- You MAY raise fresh minor issues for small tidy-up work as you go. This SHOULD be kept to a bare minimum—avoid more than two issues per PR. </ISSUE_MANAGEMENT>
<PULL_REQUESTS>
- MUST only describe "what" was done not "why"/"how"
- MUST name the Issue or Issue(s) that they close in a manner that causes a PR merge to close the issue(s).
- MUST NOT repeat details that are already in the Issue.
- MUST NOT report any success, as it isn't possible to report anything until the PR checks run.
- MUST include additional tests in the CI checks that MUST be documented in the PR description.
- MUST be changed to status
Draftif the PR checks fail. </PULL_REQUESTS>
The project currently uses a simple, guarded, semi-manual release. Automation via tags is deferred until upstream activity picks up, at which point there is a draft github action that needs finishing off.
Steps (run each line individually)
test -z "$(git status --porcelain)" && echo "✅ Success" || echo "🛑 Working tree not clean; commit or stash changes first"
VERSION="$(awk -F= '/^VERSION=/{print $2; exit}' .env)"; echo "$VERSION"
git checkout -b "rel-$VERSION" && echo "✅ Success" || echo "🛑 Branch already exists did you bump the version after you completed the last release?"
mvnd -q versions:set -DnewVersion="$VERSION" && echo "✅ Success" || echo "🛑 Unable to set the new versions"
git commit -am "chore: release $VERSION (branch-local version bump)" && echo "✅ Success" || echo "🛑 Nothing to commit; did you set the same version as already in the POM?"
git tag -a "release/$VERSION" -m "release $VERSION" && echo "✅ Success" || echo "🛑 Tag already exists; did you bump the version after you completed the last release?"
test "$(git cat-file -t "release/$VERSION")" = "tag" && echo "✅ Success" || echo "🛑 Tag not found; did you mistype the version?"
test "$(git rev-parse "release/$VERSION^{commit}")" = "$(git rev-parse HEAD)" && echo "✅ Success" || echo "🛑 Tag does not point to HEAD; did you mistype the version?"
git push origin "release/$VERSION" && echo "✅ Success" || echo "🛑 Unable to push tag; do you have permission to push to this repo?"
gh release create "release/$VERSION" --generate-notes -t "release $VERSION" && echo "✅ Success" || echo "🛑 Unable to create the GitHub Release; do you have permission to push to this repo?"
set -a; . ./.env; set +a
KEYARG=""; [ -n "$GPG_KEYNAME" ] && KEYARG="-Dgpg.keyname=$GPG_KEYNAME"
mvnd -P release -Dgpg.passphrase="$GPG_PASSPHRASE" $KEYARG clean deploy && echo "✅ Success" || echo "🛑 Unable to deploy to Maven Central; check the output for details"
git push -u origin "rel-$VERSION" && echo "✅ Success" || echo "🛑 Unable to push branch; do you have permission to push to this repo?"
If fixes occur after tagging
- git tag -d "release/$VERSION"
- git tag -a "release/$VERSION" -m "release $VERSION"
- git push -f origin "release/$VERSION"
Notes
- .env holds VERSION, GPG_PASSPHRASE, and optionally GPG_KEYNAME. It should not be committed.
- No SNAPSHOT bump to main. Version selection is driven by the tag and GitHub Release.
- The release profile (-P release) scopes signing/publishing; daily jobs don’t invoke GPG.