From e8d2a1d06a68ba3a3af1bde6a228c945e2d8ae4a Mon Sep 17 00:00:00 2001 From: Tyler Pina Date: Mon, 11 May 2026 11:09:33 -0600 Subject: [PATCH 1/3] docs: seed Golden Context Adds ARCHITECTURE.md, AGENTS.md, 7 ADRs, .bito config, docs/specs/ scaffold, and enriches CONTRIBUTING.md and README.md with agent-visible context. Co-Authored-By: Claude Sonnet 4.6 --- .bito.yaml | 19 +++ .bito/guidelines/domain-invariants.txt | 21 ++++ .../guidelines/repo-truth-and-boundaries.txt | 11 ++ .bito/guidelines/review-posture.txt | 11 ++ AGENTS.md | 60 ++++++++++ ARCHITECTURE.md | 108 ++++++++++++++++++ CONTRIBUTING.md | 65 +++++++++++ README.md | 16 +++ .../0001-module-per-resource-architecture.md | 32 ++++++ docs/ADRs/0002-retrofit2-as-http-client.md | 34 ++++++ docs/ADRs/0003-rxjava2-async-pattern.md | 34 ++++++ docs/ADRs/0004-java8-android-compatibility.md | 31 +++++ ...05-kotlin-for-tests-java-for-production.md | 30 +++++ docs/ADRs/0006-devcontainer-for-ci-parity.md | 30 +++++ docs/ADRs/0007-maven-central-publishing.md | 30 +++++ docs/ADRs/README.md | 24 ++++ docs/specs/.gitkeep | 0 docs/specs/README.md | 16 +++ 18 files changed, 572 insertions(+) create mode 100644 .bito.yaml create mode 100644 .bito/guidelines/domain-invariants.txt create mode 100644 .bito/guidelines/repo-truth-and-boundaries.txt create mode 100644 .bito/guidelines/review-posture.txt create mode 100644 AGENTS.md create mode 100644 ARCHITECTURE.md create mode 100644 docs/ADRs/0001-module-per-resource-architecture.md create mode 100644 docs/ADRs/0002-retrofit2-as-http-client.md create mode 100644 docs/ADRs/0003-rxjava2-async-pattern.md create mode 100644 docs/ADRs/0004-java8-android-compatibility.md create mode 100644 docs/ADRs/0005-kotlin-for-tests-java-for-production.md create mode 100644 docs/ADRs/0006-devcontainer-for-ci-parity.md create mode 100644 docs/ADRs/0007-maven-central-publishing.md create mode 100644 docs/ADRs/README.md create mode 100644 docs/specs/.gitkeep create mode 100644 docs/specs/README.md diff --git a/.bito.yaml b/.bito.yaml new file mode 100644 index 00000000..503d6ce4 --- /dev/null +++ b/.bito.yaml @@ -0,0 +1,19 @@ +suggestion_mode: comprehensive +post_description: true +post_changelist: true +exclude_files: 'pom.xml.versionsBackup' +exclude_draft_pr: false +secret_scanner_feedback: true +linters_feedback: true +repo_level_guidelines_enabled: true +sequence_diagram_enabled: true +custom_guidelines: + general: + - name: 'Review Posture' + path: './.bito/guidelines/review-posture.txt' + - name: 'Repo Truth And Alignment' + path: './.bito/guidelines/repo-truth-and-boundaries.txt' + - name: 'Domain Invariants' + path: './.bito/guidelines/domain-invariants.txt' + +# Generated by seed-golden-context | Last updated: 2026-05-11 diff --git a/.bito/guidelines/domain-invariants.txt b/.bito/guidelines/domain-invariants.txt new file mode 100644 index 00000000..431f7803 --- /dev/null +++ b/.bito/guidelines/domain-invariants.txt @@ -0,0 +1,21 @@ +Critical domain invariants for contentful-management.java. Violations of these rules will cause runtime failures, silent data loss, or consumer-facing breakage. + +1. ENVIRONMENT SCOPING: The modules apiKeys, environments, roles, spaceMemberships, uiExtensions, uploads, and webhooks do NOT support non-master environments. If a module that extends AbsModule operates in a space-only context, it must call throwIfEnvironmentIdIsSet() in its constructor or before any operation. Failing to do this causes CMANotWithEnvironmentsException at runtime. + +2. PUBLIC API STABILITY: All CMA* model classes, CMAClient builder methods, and Module* public methods are part of the published public API. Removing or renaming them is a breaking change. Adding required parameters to existing methods is a breaking change. Check the CHANGELOG and semver implications before modifying these. + +3. JAVA 8 TARGET: Production code in src/main/java/ must compile and run on Java 8 and Android API 21+. Do not use Java 9+ APIs (var, List.of(), Map.of(), Optional.ifPresentOrElse(), etc.) or any class not present on Android API 21. Tests in src/test/kotlin/ are exempt. + +4. GSON TYPE ADAPTERS: All CMA model types with non-trivial wire formats have custom Gson type adapters registered in CMAClient. If you add a new model type that needs custom serialization, register its adapter in CMAClient — it will not be picked up automatically. + +5. RICH TEXT HIERARCHY: The CMA enforces a strict rich text node hierarchy. The SDK models it but does NOT validate at write time. Document cannot nest Document; Links must be inside Paragraphs; Lists must be inside Document; ListItems must contain at least one Paragraph. Violations produce a 422 from the API, not an SDK exception. + +6. CALLBACK DEFAULT: CMACallback.onFailure() has an empty default implementation. Callers who do not override it will silently swallow errors. When reviewing async code, check that failures are handled. + +7. CHECKSTYLE: checkstyle.xml at the repo root is the enforced style config. All Java production code must pass it. Do not disable checkstyle rules without a strong documented reason. + +8. MODULE PATTERN: Every new CMA resource type needs both a Module*.java (public API) and a Service*.java (Retrofit interface). The module must be registered in CMAClient. Do not bypass AbsModule. + +9. SENSITIVE LOGGING: The logSensitiveData flag in CMAClient controls whether Authorization headers appear in logs. Default is false. Never change this default or log auth tokens unconditionally. + +# Generated by seed-golden-context | Last updated: 2026-05-11 diff --git a/.bito/guidelines/repo-truth-and-boundaries.txt b/.bito/guidelines/repo-truth-and-boundaries.txt new file mode 100644 index 00000000..16ab9221 --- /dev/null +++ b/.bito/guidelines/repo-truth-and-boundaries.txt @@ -0,0 +1,11 @@ +Use the repository's written documentation as review context and check whether the change matches the documented intent. + +- Start from README.md, ARCHITECTURE.md, AGENTS.md, CONTRIBUTING.md, and docs/ADRs/ for architectural context. +- Check whether code, tests, and documentation all tell the same story. Flag mismatches between implementation and the documented architecture or ADRs. +- Treat AGENTS.md as the authoritative guide for sharp edges and invariants. If a change violates an invariant documented there, flag it. +- If CI or another required check already enforces a merge rule, do not ask for duplicate PR template sections or manual checklists. +- Ask for an ADR update when a change is architecture-significant: new module, new runtime dependency, new async pattern, new publishing target, changes to environment scoping rules. +- Distinguish the current public Java API from internal implementation details. Changes to public `CMA*` model classes, `CMAClient` builder options, or `Module*` method signatures require extra scrutiny — this is a published library with downstream consumers. +- The `Service*.java` interfaces are Retrofit annotations and define the exact HTTP contract with the CMA. Changes here must match the actual CMA API behavior. + +# Generated by seed-golden-context | Last updated: 2026-05-11 diff --git a/.bito/guidelines/review-posture.txt b/.bito/guidelines/review-posture.txt new file mode 100644 index 00000000..a8b56414 --- /dev/null +++ b/.bito/guidelines/review-posture.txt @@ -0,0 +1,11 @@ +Review this pull request like the tech lead of the contentful-management.java project — a Java SDK for Contentful's Content Management API, owned by the Developer Experience team. + +- Prefer a few high-signal findings to a long list of minor or style-only comments. +- Prefer behavior, contract, runtime, and documentation issues over process-only suggestions. Do not ask for duplicate PR template sections or manual validation acknowledgements when CI or required checks already enforce that policy. +- Keep feedback actionable: explain why it matters, how it would surface in practice, and the clearest next step. +- If a concern is only a risk or assumption rather than a confirmed bug, say that clearly and explain what evidence would confirm it. +- If you find no issues, say so explicitly and call out any residual uncertainty that still deserves human attention. +- Pay special attention to public API surface changes — this is a published library. Breaking changes require strong justification and must be clearly called out. +- Checkstyle enforcement is automatic — do not comment on code style if the CI checkstyle gate already catches it. + +# Generated by seed-golden-context | Last updated: 2026-05-11 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..199d882b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,60 @@ +# Agent Guide + + + +Read this file first. It tells you where to find context in this repo. + +## Quick Reference + +| What you need | Where to look | +|---|---| +| How this repo is structured | [ARCHITECTURE.md](./ARCHITECTURE.md) | +| How to build/test/run | [CONTRIBUTING.md](./CONTRIBUTING.md) | +| Why decisions were made | [docs/ADRs/](./docs/ADRs/) | +| What this repo does | [README.md](./README.md) | +| PR review rules | [.bito/guidelines/](./.bito/guidelines/) | +| Active specs/work | [docs/specs/](./docs/specs/) | + +## Sharp Edges & Invariants + +- **Never call environment-scoped operations on the wrong modules.** The modules `apiKeys`, `environments`, `roles`, `spaceMemberships`, `uiExtensions`, `uploads`, and `webhooks` throw `CMANotWithEnvironmentsException` if the client is configured with a non-`master` `environmentId`. Always use a separate client (no `environmentId`) for those modules. +- **Java 8 is the minimum target — do not use Java 9+ APIs in production code.** The Android dependency (`com.google.android:android`) is marked `optional` but must remain compilable on API 21+. Tests may use newer JVM features. +- **Do not add new runtime dependencies without an ADR.** The dependency footprint is deliberately small to keep Android app size predictable. +- **Every Module must extend `AbsModule`.** The base class handles argument validation, executor injection, and space/environment ID pre-configuration. Never bypass it. +- **Custom Gson adapters must be registered in `CMAClient`.** The Gson instance is built once in `CMAClient` and shared across all modules. New model types with non-trivial wire formats need a type adapter registered there. +- **RichText hierarchy rules are enforced by the API** — the SDK models them but does not validate at write time. Violating the hierarchy (Document in Document, Link outside Paragraph, etc.) will produce a 422 from the CMA. See `ARCHITECTURE.md` for the full rule set. +- **The `CMACallback.onFailure()` default implementation is empty.** Code that needs to handle failures must override it explicitly. This is a common source of silently dropped errors. +- **Test files are Kotlin; production code is Java.** Keep this split. Do not introduce Kotlin into `src/main/java/`. +- **Checkstyle is enforced on every build (`./mvnw verify`).** Violations fail CI. The config is in `checkstyle.xml` at the repo root. + +## Key Conventions + +- **Commit format:** Conventional Commits (`feat:`, `fix:`, `chore:`, `docs:`, etc.) — not enforced by a hook, but followed by convention +- **Branch strategy:** `master` is the main branch; feature/fix branches merged via PR; releases use `release/X.Y.Z` branches +- **Test location:** `src/test/kotlin/com/contentful/java/cma/` (unit) and `src/test/kotlin/com/contentful/java/cma/e2e/` (integration) +- **Build system:** Maven Wrapper (`./mvnw`) — always use the wrapper, never a system Maven install +- **Module pattern:** One `Module*.java` + one `Service*.java` per CMA resource type +- **Model naming:** All resource model classes are prefixed `CMA` (e.g., `CMAEntry`, `CMASpace`) + +## Integration Points + +**Upstream (this repo consumes):** +- Contentful CMA (`api.contentful.com`) — all content management operations +- Contentful Upload API (`upload.contentful.com`) — binary asset upload + +**Downstream (consumes this repo):** +- Java and Android applications that manage Contentful content +- Published to Maven Central as `com.contentful.java:cma-sdk` + +## Build & Quality + +```bash +# Full verification (tests + checkstyle) inside devcontainer +./mvnw -B test + +# Run tests only (skip checkstyle) +./mvnw -B -DskipTests=false test + +# Verify with checkstyle +./mvnw -B verify +``` diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 00000000..34eeb716 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,108 @@ +# Architecture + + + +## Overview + +`contentful-management.java` is the official Java SDK for Contentful's [Content Management API (CMA)](https://www.contentful.com/developers/docs/references/content-management-api/). It provides a strongly-typed, fluent Java client that wraps every CMA endpoint, supports both synchronous and asynchronous (RxJava2-backed) call patterns, and runs on Java 8+ including Android (API 21+). + +## System Context + +```mermaid +graph TD + App["Java / Android Application"] --> CMAClient["CMAClient (this SDK)"] + CMAClient --> Retrofit["Retrofit2 + OkHttp"] + Retrofit --> CMA["Contentful CMA\n(api.contentful.com)"] + CMAClient --> Gson["Gson (de/serialization)"] + CMAClient --> RxJava2["RxJava2 (async adapter)"] +``` + +**Upstream:** Contentful Content Management API — authenticated via Personal Access Token (PAT) or OAuth token passed as `Authorization: Bearer `. + +**Downstream consumers:** Any Java or Android application that needs to programmatically create, read, update, or delete Contentful content, spaces, environments, or configuration resources. + +## Internal Structure + +| Directory / Package | Purpose | +|---|---| +| `src/main/java/com/contentful/java/cma/` | Core SDK: `CMAClient`, all `Module*` and `Service*` classes, interceptors, Gson adapters | +| `src/main/java/com/contentful/java/cma/model/` | All `CMA*` resource model POJOs (entries, assets, content types, etc.) | +| `src/main/java/com/contentful/java/cma/model/rich/` | Rich Text node hierarchy models | +| `src/main/java/com/contentful/java/cma/gson/` | Custom Gson serializers and deserializers for CMA-specific wire formats | +| `src/main/java/com/contentful/java/cma/interceptor/` | OkHttp interceptors: auth header, content-type, rate-limit, error, logging, user-agent | +| `src/main/resources/com/contentful/java/cma/build/` | Build-time generated `GeneratedBuildParameters.java` containing `PROJECT_VERSION` | +| `src/test/kotlin/com/contentful/java/cma/` | Unit tests (Kotlin + JUnit 4 + OkHttp MockWebServer) | +| `src/test/kotlin/com/contentful/java/cma/e2e/` | End-to-end integration tests (require live CMA credentials) | +| `src/test/resources/` | Mock JSON response fixtures for unit tests | +| `.devcontainer/` | Devcontainer config (Temurin JDK 8, multi-arch) — used by both local dev and CI | +| `.github/workflows/` | GitHub Actions CI: runs `./mvnw -B test` inside devcontainer | + +## Module / Service Pattern + +Each CMA resource type has two classes: + +- **`Module.java`** — public-facing API: validates arguments, wraps synchronous `service.*` calls, and exposes an `.async()` accessor returning a callback-based counterpart. +- **`Service.java`** — Retrofit2 `@interface` defining the raw HTTP endpoints for that resource. + +All modules extend `AbsModule` which holds references to the Retrofit service, callback executor, and optionally pre-configured `spaceId` / `environmentId`. + +``` +CMAClient + └── builds Retrofit instance (with interceptor chain) + └── instantiates one Module per resource type + └── Module delegates to Service (Retrofit interface) + └── results wrapped via RxJava2 Observable +``` + +## Data Flow + +1. **Client creation** — `CMAClient.Builder` assembles an OkHttp client with auth, user-agent, content-type, rate-limit, error, and logging interceptors, then builds a Retrofit instance backed by Gson + RxJava2. +2. **Synchronous call** — `client.entries().fetchAll(spaceId, environmentId)` → Module validates args → calls Retrofit service → blocks via `SynchronousExecutor` → returns typed result. +3. **Asynchronous call** — `client.entries().async().fetchAll(...)` → same service call → emits via RxJava2 Observable → marshalled onto the provided callback `Executor` → fires `CMACallback.onSuccess` / `onFailure`. +4. **Deserialization** — Gson with custom type adapters (`CMASystemDeserializer`, `EntrySerializer`, `FieldTypeAdapter`, etc.) converts CMA JSON wire format into `CMA*` POJOs. +5. **Error handling** — `ErrorInterceptor` converts non-2xx HTTP responses into `CMAHttpException`; `RateLimitInterceptor` surfaces rate-limit headers via `RateLimitsListener`. + +## Key Dependencies + +| Dependency | Why it's here | +|---|---| +| `retrofit2` (2.9.0) | Declarative HTTP client; supports interceptor chain and multiple call adapters. See [ADR-0002](./docs/ADRs/0002-retrofit2-as-http-client.md) | +| `retrofit2:adapter-rxjava2` | Enables async Observable-based responses bridging to CMACallback. See [ADR-0003](./docs/ADRs/0003-rxjava2-async-pattern.md) | +| `retrofit2:converter-gson` | JSON serialization; Gson chosen for Android compatibility and custom type adapter support | +| `com.google.code.gson` (2.8.9) | Custom de/serialization for CMA-specific types (rich text, snapshots, webhooks) | +| `io.reactivex.rxjava2:rxjava` (2.2.5) | Reactive foundation for async call pattern | +| `kotlin-stdlib` | Used in production code (minimal); tests are written entirely in Kotlin | +| `okhttp3` (test scope) | MockWebServer for unit tests; OkHttp itself is an optional runtime dep | +| `com.google.android:android` (optional) | Android API stubs — makes SDK compatible with Android API 21+ without requiring it | +| `junit` (4.13.1) | Test runner for unit tests | +| `org.jetbrains.kotlin:kotlin-test-junit` | Kotlin test assertions in unit test suite | + +## Configuration + +| Parameter | Purpose | Default | +|---|---|---| +| `accessToken` | CMA Personal Access Token or OAuth token (required) | — | +| `spaceId` | Pre-configure space ID so modules omit it per-call | `null` (pass per-call) | +| `environmentId` | Pre-configure environment ID | `null` (defaults to `master` for most endpoints) | +| `callbackExecutor` | `Executor` for async callback delivery | `Executors.newCachedThreadPool()` | +| `coreEndpoint` | Override CMA base URL (e.g., for proxying) | `https://api.contentful.com` | +| `uploadEndpoint` | Override upload endpoint | `https://upload.contentful.com` | +| `logLevel` | `Logger.Level` — `NONE`, `BASIC`, `HEADERS`, `FULL` | `NONE` | +| `logSensitiveData` | Whether to include auth headers in logs | `false` | +| `httpClient` | Custom `okhttp3.Call.Factory` | OkHttp if on classpath, else `HttpURLConnection` | + +## Integration Points + +### Upstream (this repo consumes) + +- **Contentful CMA** (`api.contentful.com`) — all REST operations +- **Contentful Upload API** (`upload.contentful.com`) — binary asset uploads + +### Downstream (consumes this repo) + +- Any Java/Android application that needs to manage Contentful content programmatically +- Distributed via Maven Central (`com.contentful.java:cma-sdk`) and Sonatype snapshots + +## Environment Scoping Rules (Sharp Edge) + +The modules `apiKeys`, `environments`, `roles`, `spaceMemberships`, `uiExtensions`, `uploads`, and `webhooks` **do not support non-`master` environments**. If `CMAClient` is configured with a non-master `environmentId`, these modules throw `CMANotWithEnvironmentsException`. Always create a separate client without an `environmentId` for these modules. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be4eefc3..2b5587f3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,3 +30,68 @@ devcontainer exec --workspace-folder . bash 1. Fork the repository and create a branch for your change. 2. Run the relevant checks from the dev container. 3. Open a pull request with a short summary of the change and any follow-up context. + + + +## Testing + +- **Framework:** JUnit 4 (runner), Kotlin test assertions (`kotlin-test-junit`), OkHttp `MockWebServer` +- **Unit tests:** `src/test/kotlin/com/contentful/java/cma/` — each resource module has a corresponding `*Tests.kt` file +- **E2E / integration tests:** `src/test/kotlin/com/contentful/java/cma/e2e/` — require live CMA credentials (`CONTENTFUL_ACCESS_TOKEN`, `CONTENTFUL_SPACE_ID`) +- **Run all tests:** `./mvnw -B test` +- **Run specific test class:** `./mvnw -B -Dtest=EntryTests test` +- **Test fixtures:** JSON response mocks are in `src/test/resources/` + +## Code Style + +Checkstyle is enforced on every `verify` run: + +```bash +./mvnw -B verify +``` + +The configuration lives in `checkstyle.xml` at the repo root. Violations will fail the build. Fix them before pushing — CI runs `./mvnw -B test` which includes the checkstyle phase. + +## Commit Convention + +This repo follows [Conventional Commits](https://www.conventionalcommits.org/) by convention: + +``` +type(scope): description +``` + +Valid types: `feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `perf`, `ci`, `build` + +Examples: +``` +feat(entries): add PATCH support for partial updates +fix(rich-text): correct nodeType deserialization for embedded entries +chore: update OkHttp to 4.12.0 +``` + +## Branch Strategy + +- `master` — the main branch; all PRs target here +- Feature/fix branches — use descriptive names, e.g., `feat/add-taxonomy-module`, `fix/rate-limit-retry` +- Release branches — `release/X.Y.Z` used during the release process + +## Release Process + +Releases are managed via `maven-release-plugin`: + +```bash +# Prepare a release (updates pom.xml versions, creates release tag) +./mvnw release:prepare + +# Perform the release (builds, signs, and publishes to Sonatype Central) +./mvnw release:perform +``` + +Artifacts are published to Maven Central under `com.contentful.java:cma-sdk`. Pre-release snapshots are available via Sonatype snapshots repository and [jitpack.io](https://jitpack.io/#contentful/contentful-management.java/master-SNAPSHOT). + +## CI/CD + +| Job | Trigger | What it does | +|---|---|---| +| `CI / Test (Java 8)` | Push to `master`, PR to `master` | Spins up devcontainer, runs `./mvnw -B test` | +| `CodeQL` | Push / PR / weekly schedule | Static analysis for security vulnerabilities | diff --git a/README.md b/README.md index e3eabbdb..1554ee0c 100644 --- a/README.md +++ b/README.md @@ -332,6 +332,22 @@ Contentful wants to provide a safe, inclusive, welcoming, and harassment-free sp [Full Code of Conduct](https://github.com/contentful-developer-relations/community-code-of-conduct). + + +Agent & Contributor Context +=========================== + +For agents and new contributors, the following documents provide structured context for working safely in this repo: + +| Document | Purpose | +|---|---| +| [AGENTS.md](./AGENTS.md) | Agent-first guide: sharp edges, invariants, quick reference — read this first | +| [ARCHITECTURE.md](./ARCHITECTURE.md) | Internal structure, module/service pattern, data flow, key dependencies | +| [CONTRIBUTING.md](./CONTRIBUTING.md) | Dev setup, testing, code style, branch and release workflow | +| [docs/ADRs/](./docs/ADRs/) | Architecture Decision Records explaining why things look the way they do | +| [docs/specs/](./docs/specs/) | Implementation specs for active and recent work | +| [.bito/guidelines/](./.bito/guidelines/) | Automated code review rules and domain invariants | + [1]: https://www.contentful.com [2]: https://square.github.io/retrofit [3]: https://square.github.io/okhttp diff --git a/docs/ADRs/0001-module-per-resource-architecture.md b/docs/ADRs/0001-module-per-resource-architecture.md new file mode 100644 index 00000000..911b3520 --- /dev/null +++ b/docs/ADRs/0001-module-per-resource-architecture.md @@ -0,0 +1,32 @@ +# ADR-0001: Module-Per-Resource Architecture + + + +## Status + +Accepted + +## Context + +The Contentful CMA exposes ~20 distinct resource types (Entries, Assets, Content Types, Spaces, Environments, Webhooks, etc.). The SDK needed an organization strategy that would: + +- Make the public API discoverable and type-safe +- Allow independent evolution of per-resource endpoint sets +- Support both synchronous and asynchronous call patterns without duplicating logic + +## Decision + +Each CMA resource type gets exactly two classes: + +1. **`Module.java`** — the public-facing API surface. Validates arguments, delegates to the service, and exposes an `.async()` accessor for the callback-based variant. +2. **`Service.java`** — a Retrofit2 `@interface` defining the raw HTTP endpoints for that resource. + +All modules extend `AbsModule`, which injects the Retrofit instance, callback executor, and optional pre-configured `spaceId` / `environmentId`. + +`CMAClient` instantiates all modules at construction time and exposes them via named accessors (e.g., `client.entries()`, `client.assets()`). + +## Consequences + +- **Enables:** Clean, discoverable API; each module can grow independently; service interfaces serve as a machine-readable map of supported endpoints. +- **Constrains:** Adding a new CMA resource requires creating both a `Module` and `Service` class, plus registering the module in `CMAClient`. One-liner wrappers are not possible. +- **Sharp edge:** Modules that operate only at the space level (not environment level) must call `throwIfEnvironmentIdIsSet()` in `AbsModule` to prevent misconfigured client usage. diff --git a/docs/ADRs/0002-retrofit2-as-http-client.md b/docs/ADRs/0002-retrofit2-as-http-client.md new file mode 100644 index 00000000..c17b09bc --- /dev/null +++ b/docs/ADRs/0002-retrofit2-as-http-client.md @@ -0,0 +1,34 @@ +# ADR-0002: Retrofit2 as HTTP Client + + + +## Status + +Accepted + +## Context + +The SDK needs to make authenticated HTTP calls to the Contentful CMA REST API, handle JSON (de)serialization, support custom headers, and provide both sync and async execution modes. Options considered: + +- Raw `HttpURLConnection` — available on all Android versions, but low-level and requires significant boilerplate. +- Apache HttpClient — not available on Android. +- OkHttp directly — good, but requires manual JSON handling and endpoint declaration. +- Retrofit2 — declarative endpoint definitions, pluggable call adapters (RxJava2), pluggable converters (Gson), built-in OkHttp integration. + +The original version (pre-2.x) used Retrofit 1.x, later upgraded to Retrofit 2.x (see git history: "Adding retrofit to changelog", "Upgrading retrofit to 2.6.1"). + +## Decision + +Use **Retrofit2** (`2.9.0`) with: +- `converter-gson` for JSON serialization +- `adapter-rxjava2` for async observable support +- OkHttp as the underlying HTTP engine (optional but recommended; falls back to `HttpURLConnection` if absent) + +The `Service*.java` interfaces are Retrofit annotations; modules call them synchronously or subscribe via RxJava2 Observables. + +## Consequences + +- **Enables:** Declarative endpoint definitions that double as documentation; interceptor chain for auth, logging, rate-limit, and error handling; clean RxJava2 async adapter. +- **Requires:** Retrofit2 and Gson as mandatory runtime dependencies; OkHttp is optional but strongly recommended. +- **Android:** OkHttp and Retrofit2 both support Android API 21+. The optional `com.google.android:android` stub keeps compilation valid. +- **Custom HTTP client:** Users can inject a custom `okhttp3.Call.Factory` via `CMAClient.Builder.setCallFactory()` for proxy or interceptor customization. diff --git a/docs/ADRs/0003-rxjava2-async-pattern.md b/docs/ADRs/0003-rxjava2-async-pattern.md new file mode 100644 index 00000000..77b44bc2 --- /dev/null +++ b/docs/ADRs/0003-rxjava2-async-pattern.md @@ -0,0 +1,34 @@ +# ADR-0003: RxJava2 for Async Call Pattern + + + +## Status + +Accepted + +## Context + +Java SDK consumers span both server-side Java applications and Android apps. Both need async execution options, but Android has strict threading requirements (no blocking on the main thread). Options considered: + +- Futures / CompletableFuture — Java 8+, but awkward on Android and not reactive. +- Plain callbacks — simple, but no composition; requires manual thread management. +- RxJava — well-established reactive library with Android support, first-class Retrofit2 adapter. + +The original implementation used RxJava 1.x (see git history: "RxJava v1.0.11", "RxJava v1.0.13"), later upgraded to RxJava2. + +## Decision + +Use **RxJava2** (`2.2.5`) as the async execution substrate, exposed to SDK consumers via `CMACallback` (a simplified callback interface). Internally: + +1. Each module method creates an `Observable` via `RxExtensions`. +2. The observable subscribes on an I/O scheduler (`Schedulers.io()`). +3. Results are delivered to the provided `Executor` (defaulting to a cached thread pool), which marshals `onSuccess` / `onFailure` to the callback. + +Consumers do not interact with RxJava directly — they use the `CMACallback` interface. RxJava is an implementation detail. + +## Consequences + +- **Enables:** Decoupled thread management; easy Retrofit2 integration via `RxJava2CallAdapterFactory`; subscribers can be cancelled. +- **Requires:** `rxjava` and `retrofit2:adapter-rxjava2` as runtime dependencies. +- **Hidden from consumers:** The RxJava API surface is internal — callers only see `CMACallback`. This keeps the public API simple and allows future migration to a different async model. +- **Default `onFailure()` is empty:** A common footgun — callers who don't override it silently swallow errors. See AGENTS.md sharp edges. diff --git a/docs/ADRs/0004-java8-android-compatibility.md b/docs/ADRs/0004-java8-android-compatibility.md new file mode 100644 index 00000000..f9dff1e6 --- /dev/null +++ b/docs/ADRs/0004-java8-android-compatibility.md @@ -0,0 +1,31 @@ +# ADR-0004: Java 8 Minimum Target and Android Compatibility + + + +## Status + +Accepted + +## Context + +The SDK targets both server-side Java applications and Android applications. Android historically lagged the JVM ecosystem — Android API 21 (Lollipop) supports Java 8 language features with some limitations. The project must: + +- Compile and run on Java 8+ +- Be compatible with Android API 21+ (via the optional `com.google.android:android` stub) +- Avoid Java 9+ APIs (modules, `var`, new collection factories) in production code + +Git history shows an explicit decision: "ignore jdk>8 due to android dependencies" and the pom.xml pin `1.8`. + +## Decision + +- Set `1.8` in `pom.xml` for both source and target compiler flags. +- Include `com.google.android:android` as an **optional** compile-time dependency to provide Android API stubs without forcing Android as a required dependency for JVM-only consumers. +- Pin `21` as the minimum API level. +- Keep all production Java code in `src/main/java/` at Java 8 language level. Tests in `src/test/kotlin/` may use Kotlin language features freely. + +## Consequences + +- **Enables:** SDK works on Android 5.0+ (API 21) and all Java 8+ server environments without modification. +- **Constrains:** Cannot use Java 9+ APIs (`var`, `List.of()`, `Map.of()`, `Optional.ifPresentOrElse()`, etc.) in production code. New contributors accustomed to modern Java must be vigilant. +- **Testing:** Robolectric is included in test scope for Android-specific test scenarios. +- **CI:** The devcontainer uses Temurin JDK 8 base image (multi-arch, supports Apple Silicon natively) to catch any accidental Java 9+ API usage. diff --git a/docs/ADRs/0005-kotlin-for-tests-java-for-production.md b/docs/ADRs/0005-kotlin-for-tests-java-for-production.md new file mode 100644 index 00000000..b91e8be5 --- /dev/null +++ b/docs/ADRs/0005-kotlin-for-tests-java-for-production.md @@ -0,0 +1,30 @@ +# ADR-0005: Kotlin for Tests, Java for Production Code + + + +## Status + +Accepted + +## Context + +Kotlin became the preferred language for Android development and offers concise test authoring. However, the SDK is a Java library consumed by Java and Android developers. Mixing Kotlin in the public API surface would: + +- Require Kotlin stdlib as a hard consumer dependency +- Introduce Kotlin-specific null-safety annotations in public types +- Add friction for Java-only consumers + +The decision was to bring Kotlin into tests (where its conciseness helps) without polluting the production public API. + +## Decision + +- All **production code** (`src/main/java/`) remains Java. Kotlin stdlib is included as a `compile` scope dependency (for internal use) but the public API surface exposes only Java types. +- All **test code** (`src/test/kotlin/`) is written in Kotlin, using `kotlin-test-junit` and `kotlin-stdlib-jdk8` in `test` scope. +- The Maven build uses `kotlin-maven-plugin` for test compilation alongside `maven-compiler-plugin` for Java. + +## Consequences + +- **Enables:** Concise, readable tests with Kotlin assertions; no Kotlin dependency burden on pure-Java SDK consumers. +- **Requires:** `kotlin-stdlib` in compile scope (minimal); `kotlin-maven-plugin` configured for test-compile phase. +- **Sharp edge:** Do not add Kotlin files under `src/main/java/`. The Maven build does not compile Kotlin there, and it would silently be excluded. +- **Note:** `kotlin-stdlib` is listed as a compile-scope dependency in `pom.xml` — this technically means it ships with the jar. This is a known trade-off; the footprint is small relative to Retrofit + Gson. diff --git a/docs/ADRs/0006-devcontainer-for-ci-parity.md b/docs/ADRs/0006-devcontainer-for-ci-parity.md new file mode 100644 index 00000000..f037b54e --- /dev/null +++ b/docs/ADRs/0006-devcontainer-for-ci-parity.md @@ -0,0 +1,30 @@ +# ADR-0006: Devcontainer as Single Source of Truth for Dev and CI + + + +## Status + +Accepted + +## Context + +Before DX-822, contributors had to reconstruct the correct toolchain locally with no guarantee their environment matched CI. This caused environment drift and made routine contributions harder to start. The project previously used CircleCI / Travis CI without a checked-in devcontainer. + +Apple Silicon (M1/M2/M3) machines could not run x86 JDK 8 images natively — they required emulation, which was slow and unreliable. Microsoft does not publish a Java 8 variant of their devcontainer base image, requiring a custom Dockerfile. + +Reference: [PR #209](https://github.com/contentful/contentful-management.java/pull/209) (`chore: add devcontainer contributor workflow [DX-822]`). + +## Decision + +- Add `.devcontainer/Dockerfile` and `.devcontainer/devcontainer.json` using the [Eclipse Temurin JDK 8](https://hub.docker.com/_/eclipse-temurin) base image (multi-arch: supports both x86_64 and arm64). +- The Dockerfile sets up a `vscode` user and installs `curl`, `git`, and `openssh-client` (not provided by the minimal Temurin base). +- `postCreateCommand` pre-fetches Maven dependencies offline: `./mvnw -B -q -DskipTests dependency:go-offline`. +- GitHub Actions CI (`ci.yml`) runs tests inside the devcontainer via `devcontainer exec` — the exact same container contributors use locally. +- Remove stale CircleCI / Travis CI configuration. + +## Consequences + +- **Enables:** Full environment parity between local dev and CI; Apple Silicon contributors can run the environment natively without emulation. +- **Requires:** Docker and the Dev Container CLI (`npm install -g @devcontainers/cli`) for local use; GitHub Actions uses `devcontainers/ci`. +- **Trade-off:** CI now requires a Docker build step, adding some overhead vs. a direct JDK setup. The parity benefit outweighs the cost. +- **VS Code:** `redhat.java` extension is auto-installed in the devcontainer for IDE support. diff --git a/docs/ADRs/0007-maven-central-publishing.md b/docs/ADRs/0007-maven-central-publishing.md new file mode 100644 index 00000000..5cdc34b7 --- /dev/null +++ b/docs/ADRs/0007-maven-central-publishing.md @@ -0,0 +1,30 @@ +# ADR-0007: Maven Central Publishing via Sonatype Central Portal + + + +## Status + +Accepted + +## Context + +Java library consumers expect artifacts to be available on Maven Central — the de facto standard repository. Pre-release snapshot consumers need a stable snapshot repository. The SDK also needed GPG signing for Central compliance and source + Javadoc JAR attachment for IDE integration. + +## Decision + +Use `central-publishing-maven-plugin` (`org.sonatype.central`, v0.8.0) targeting the Sonatype Central Portal (`publishingServerId: central`). The release pipeline: + +1. `maven-release-plugin` manages version bumps, tagging, and release commits. +2. `maven-source-plugin` attaches sources JAR (`-sources.jar`). +3. `maven-javadoc-plugin` attaches Javadoc JAR (`-javadoc.jar`). +4. `maven-gpg-plugin` signs all artifacts in the `verify` phase. +5. `central-publishing-maven-plugin` publishes the signed bundle to Maven Central. + +Pre-release snapshots go to `https://oss.sonatype.org/content/repositories/snapshots/`. `jitpack.io` is also supported as an alternative snapshot source. + +## Consequences + +- **Enables:** Standard Maven/Gradle dependency coordinates (`com.contentful.java:cma-sdk:X.Y.Z`); IDE source navigation via sources JAR; Javadoc in IDE tooltips. +- **Requires:** GPG key available in the CI/release environment; Sonatype Central credentials configured as Maven server settings. +- **Release branch pattern:** Releases use `release/X.Y.Z` branches with `[maven-release-plugin]` commits visible in git history. +- **Jitpack fallback:** `jitpack.yml` at the repo root enables snapshot builds from any branch via jitpack.io. diff --git a/docs/ADRs/README.md b/docs/ADRs/README.md new file mode 100644 index 00000000..6fb8ac35 --- /dev/null +++ b/docs/ADRs/README.md @@ -0,0 +1,24 @@ +# Architecture Decision Records + + + +This directory records significant architectural decisions made in `contentful-management.java`. Each ADR explains what was decided, why, and what trade-offs were accepted. + +## Index + +| ADR | Title | Status | +|---|---|---| +| [0001](./0001-module-per-resource-architecture.md) | Module-Per-Resource Architecture | Accepted | +| [0002](./0002-retrofit2-as-http-client.md) | Retrofit2 as HTTP Client | Accepted | +| [0003](./0003-rxjava2-async-pattern.md) | RxJava2 for Async Call Pattern | Accepted | +| [0004](./0004-java8-android-compatibility.md) | Java 8 Minimum Target and Android Compatibility | Accepted | +| [0005](./0005-kotlin-for-tests-java-for-production.md) | Kotlin for Tests, Java for Production Code | Accepted | +| [0006](./0006-devcontainer-for-ci-parity.md) | Devcontainer as Single Source of Truth for Dev and CI | Accepted | +| [0007](./0007-maven-central-publishing.md) | Maven Central Publishing via Sonatype Central Portal | Accepted | + +## How to Add a New ADR + +1. Copy the ADR template format (Status, Context, Decision, Consequences). +2. Number it sequentially (next: `0008`). +3. Add it to the index above. +4. Link to it from `AGENTS.md` if it introduces a new sharp edge or invariant. diff --git a/docs/specs/.gitkeep b/docs/specs/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/specs/README.md b/docs/specs/README.md new file mode 100644 index 00000000..51d87bc4 --- /dev/null +++ b/docs/specs/README.md @@ -0,0 +1,16 @@ +# Specs + + + +This directory holds implementation-level specs for active and recent work in `contentful-management.java`. + +## Conventions + +- File naming: `YYYY-MM-DD-.md` +- Each spec should document: what is being built, why, the proposed API surface or behavior, and any open questions. +- Mark completed work with `status: done` in the spec frontmatter or a note at the top. +- Specs are inputs to Golden Context — if a spec captures a durable decision, promote it to an ADR in `docs/ADRs/`. + +## Active Specs + +_No active specs yet. Add one when starting a new feature or significant change._ From 054bf9e6893d09ece07025b2871d412b49256e75 Mon Sep 17 00:00:00 2001 From: Tyler Pina Date: Tue, 12 May 2026 11:11:26 -0600 Subject: [PATCH 2/3] docs: fix checkstyle phase claim and kotlin-stdlib scope description - CONTRIBUTING.md: CI uses `./mvnw -B verify` for checkstyle, not `./mvnw -B test` - AGENTS.md: relabel build commands so `./mvnw -B test` is NOT described as "full verification"; `./mvnw -B verify` is the full check - ADR-0005: correct kotlin-stdlib framing from "internal use" to "transitive runtime dependency for consumers", with Android jar-size callout --- AGENTS.md | 7 ++----- CONTRIBUTING.md | 2 +- docs/ADRs/0005-kotlin-for-tests-java-for-production.md | 6 +++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 199d882b..1d910f26 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -49,12 +49,9 @@ Read this file first. It tells you where to find context in this repo. ## Build & Quality ```bash -# Full verification (tests + checkstyle) inside devcontainer +# Run tests (does NOT include checkstyle) ./mvnw -B test -# Run tests only (skip checkstyle) -./mvnw -B -DskipTests=false test - -# Verify with checkstyle +# Full verification (tests + checkstyle) inside devcontainer ./mvnw -B verify ``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2b5587f3..eec38e80 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,7 +50,7 @@ Checkstyle is enforced on every `verify` run: ./mvnw -B verify ``` -The configuration lives in `checkstyle.xml` at the repo root. Violations will fail the build. Fix them before pushing — CI runs `./mvnw -B test` which includes the checkstyle phase. +The configuration lives in `checkstyle.xml` at the repo root. Violations will fail the build. Fix them before pushing — CI runs `./mvnw -B verify` to include the checkstyle phase (not `./mvnw -B test`, which skips checkstyle). ## Commit Convention diff --git a/docs/ADRs/0005-kotlin-for-tests-java-for-production.md b/docs/ADRs/0005-kotlin-for-tests-java-for-production.md index b91e8be5..f3470909 100644 --- a/docs/ADRs/0005-kotlin-for-tests-java-for-production.md +++ b/docs/ADRs/0005-kotlin-for-tests-java-for-production.md @@ -18,13 +18,13 @@ The decision was to bring Kotlin into tests (where its conciseness helps) withou ## Decision -- All **production code** (`src/main/java/`) remains Java. Kotlin stdlib is included as a `compile` scope dependency (for internal use) but the public API surface exposes only Java types. +- All **production code** (`src/main/java/`) remains Java. Kotlin stdlib is included as a `compile` scope dependency — meaning it is a **transitive runtime dependency for consumers**: any project that declares this SDK as a dependency will receive `kotlin-stdlib` on its classpath. This has an Android jar-size implication. The public API surface still exposes only Java types. - All **test code** (`src/test/kotlin/`) is written in Kotlin, using `kotlin-test-junit` and `kotlin-stdlib-jdk8` in `test` scope. - The Maven build uses `kotlin-maven-plugin` for test compilation alongside `maven-compiler-plugin` for Java. ## Consequences - **Enables:** Concise, readable tests with Kotlin assertions; no Kotlin dependency burden on pure-Java SDK consumers. -- **Requires:** `kotlin-stdlib` in compile scope (minimal); `kotlin-maven-plugin` configured for test-compile phase. +- **Requires:** `kotlin-stdlib` in compile scope — it is a transitive dependency for all SDK consumers, not just for internal build use. Android consumers should be aware of the additional jar size impact relative to Retrofit + Gson. - **Sharp edge:** Do not add Kotlin files under `src/main/java/`. The Maven build does not compile Kotlin there, and it would silently be excluded. -- **Note:** `kotlin-stdlib` is listed as a compile-scope dependency in `pom.xml` — this technically means it ships with the jar. This is a known trade-off; the footprint is small relative to Retrofit + Gson. +- **Note:** `kotlin-stdlib` in compile scope means it is resolved and placed on the classpath of any downstream project that depends on this SDK. This is a known trade-off; the footprint is small relative to other transitive deps. From b613e6be77bdf58013a46667c8c138d587db9263 Mon Sep 17 00:00:00 2001 From: Tyler Pina Date: Tue, 12 May 2026 11:26:49 -0600 Subject: [PATCH 3/3] docs: address Bito review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix module list in domain-invariants.txt: remove environments/uiExtensions (they support environment scope), add previewApiKeys (verified by grep) - Fix ADR 0005 sharp edge: Kotlin plugin does scan src/main/java in sourceDirs; clarify that .kt files must not be added there - Fix CONTRIBUTING.md: CI runs ./mvnw -B test, not verify — checkstyle is not enforced in CI - Fix README.md: convert Agent & Contributor Context heading from === underline style to ## to match rest of file - Add missing AGENTS.md invariants: public API stability rules and sensitive logging constraint - Correct AGENTS.md checkstyle note: violations do not fail CI (CI skips verify) Co-Authored-By: Claude Opus 4.6 --- .bito/guidelines/domain-invariants.txt | 2 +- AGENTS.md | 6 ++++-- CONTRIBUTING.md | 2 +- README.md | 3 +-- docs/ADRs/0005-kotlin-for-tests-java-for-production.md | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.bito/guidelines/domain-invariants.txt b/.bito/guidelines/domain-invariants.txt index 431f7803..e2ec73dd 100644 --- a/.bito/guidelines/domain-invariants.txt +++ b/.bito/guidelines/domain-invariants.txt @@ -1,6 +1,6 @@ Critical domain invariants for contentful-management.java. Violations of these rules will cause runtime failures, silent data loss, or consumer-facing breakage. -1. ENVIRONMENT SCOPING: The modules apiKeys, environments, roles, spaceMemberships, uiExtensions, uploads, and webhooks do NOT support non-master environments. If a module that extends AbsModule operates in a space-only context, it must call throwIfEnvironmentIdIsSet() in its constructor or before any operation. Failing to do this causes CMANotWithEnvironmentsException at runtime. +1. ENVIRONMENT SCOPING: The modules apiKeys, previewApiKeys, roles, spaceMemberships, uploads, and webhooks do NOT support non-master environments. If a module that extends AbsModule operates in a space-only context, it must call throwIfEnvironmentIdIsSet() in its constructor or before any operation. Failing to do this causes CMANotWithEnvironmentsException at runtime. 2. PUBLIC API STABILITY: All CMA* model classes, CMAClient builder methods, and Module* public methods are part of the published public API. Removing or renaming them is a breaking change. Adding required parameters to existing methods is a breaking change. Check the CHANGELOG and semver implications before modifying these. diff --git a/AGENTS.md b/AGENTS.md index 1d910f26..8a102a16 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ Read this file first. It tells you where to find context in this repo. ## Sharp Edges & Invariants -- **Never call environment-scoped operations on the wrong modules.** The modules `apiKeys`, `environments`, `roles`, `spaceMemberships`, `uiExtensions`, `uploads`, and `webhooks` throw `CMANotWithEnvironmentsException` if the client is configured with a non-`master` `environmentId`. Always use a separate client (no `environmentId`) for those modules. +- **Never call environment-scoped operations on the wrong modules.** The modules `apiKeys`, `previewApiKeys`, `roles`, `spaceMemberships`, `uploads`, and `webhooks` throw `CMANotWithEnvironmentsException` if the client is configured with a non-`master` `environmentId`. Always use a separate client (no `environmentId`) for those modules. - **Java 8 is the minimum target — do not use Java 9+ APIs in production code.** The Android dependency (`com.google.android:android`) is marked `optional` but must remain compilable on API 21+. Tests may use newer JVM features. - **Do not add new runtime dependencies without an ADR.** The dependency footprint is deliberately small to keep Android app size predictable. - **Every Module must extend `AbsModule`.** The base class handles argument validation, executor injection, and space/environment ID pre-configuration. Never bypass it. @@ -25,7 +25,9 @@ Read this file first. It tells you where to find context in this repo. - **RichText hierarchy rules are enforced by the API** — the SDK models them but does not validate at write time. Violating the hierarchy (Document in Document, Link outside Paragraph, etc.) will produce a 422 from the CMA. See `ARCHITECTURE.md` for the full rule set. - **The `CMACallback.onFailure()` default implementation is empty.** Code that needs to handle failures must override it explicitly. This is a common source of silently dropped errors. - **Test files are Kotlin; production code is Java.** Keep this split. Do not introduce Kotlin into `src/main/java/`. -- **Checkstyle is enforced on every build (`./mvnw verify`).** Violations fail CI. The config is in `checkstyle.xml` at the repo root. +- **Public API surface is stable — treat changes as breaking.** All `CMA*` model classes, `CMAClient` builder methods, and `Module*` public methods are part of the published API. Removing, renaming, or adding required parameters to any of these is a breaking change and requires a semver major bump. Review CHANGELOG implications before modifying them. +- **Never log auth tokens unconditionally.** The `logSensitiveData` flag in `CMAClient` controls whether `Authorization` headers appear in logs. Its default is `false`. Do not change that default or add unconditional logging of auth/credentials anywhere in the SDK. +- **Checkstyle runs during `./mvnw verify` (not `./mvnw test`).** CI only runs `./mvnw -B test`, so checkstyle is not enforced in CI — run `./mvnw -B verify` locally before pushing to catch violations. The config is in `checkstyle.xml` at the repo root. ## Key Conventions diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eec38e80..060f4a13 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,7 +50,7 @@ Checkstyle is enforced on every `verify` run: ./mvnw -B verify ``` -The configuration lives in `checkstyle.xml` at the repo root. Violations will fail the build. Fix them before pushing — CI runs `./mvnw -B verify` to include the checkstyle phase (not `./mvnw -B test`, which skips checkstyle). +The configuration lives in `checkstyle.xml` at the repo root. Violations will fail the build. Run `./mvnw -B verify` locally before pushing to catch checkstyle violations — CI only runs `./mvnw -B test`, which skips the checkstyle phase. ## Commit Convention diff --git a/README.md b/README.md index 1554ee0c..3c73b5f9 100644 --- a/README.md +++ b/README.md @@ -334,8 +334,7 @@ Contentful wants to provide a safe, inclusive, welcoming, and harassment-free sp -Agent & Contributor Context -=========================== +## Agent & Contributor Context For agents and new contributors, the following documents provide structured context for working safely in this repo: diff --git a/docs/ADRs/0005-kotlin-for-tests-java-for-production.md b/docs/ADRs/0005-kotlin-for-tests-java-for-production.md index f3470909..52cb6f88 100644 --- a/docs/ADRs/0005-kotlin-for-tests-java-for-production.md +++ b/docs/ADRs/0005-kotlin-for-tests-java-for-production.md @@ -26,5 +26,5 @@ The decision was to bring Kotlin into tests (where its conciseness helps) withou - **Enables:** Concise, readable tests with Kotlin assertions; no Kotlin dependency burden on pure-Java SDK consumers. - **Requires:** `kotlin-stdlib` in compile scope — it is a transitive dependency for all SDK consumers, not just for internal build use. Android consumers should be aware of the additional jar size impact relative to Retrofit + Gson. -- **Sharp edge:** Do not add Kotlin files under `src/main/java/`. The Maven build does not compile Kotlin there, and it would silently be excluded. +- **Sharp edge:** Do not add Kotlin files under `src/main/java/`. Although `kotlin-maven-plugin`'s compile execution lists `src/main/java` in its `sourceDirs`, the directory contains only `.java` files and no `.kt` files should ever be added there. Doing so would silently introduce Kotlin into the production source set, contradicting this ADR. - **Note:** `kotlin-stdlib` in compile scope means it is resolved and placed on the classpath of any downstream project that depends on this SDK. This is a known trade-off; the footprint is small relative to other transitive deps.